--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2000, 2012 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.corext.refactoring;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourceAttributes;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.resources.mapping.IResourceChangeDescriptionFactory;
+
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
+import org.eclipse.ltk.core.refactoring.participants.ResourceChangeChecker;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaModelMarker;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.ILocalVariable;
+import org.eclipse.jdt.core.IMember;
+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.Signature;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.Annotation;
+import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.IBinding;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.SwitchCase;
+import org.eclipse.jdt.core.dom.VariableDeclaration;
+
+import org.eclipse.jdt.internal.corext.Corext;
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
+import org.eclipse.jdt.internal.corext.dom.Bindings;
+import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext;
+import org.eclipse.jdt.internal.corext.refactoring.base.RefactoringStatusCodes;
+import org.eclipse.jdt.internal.corext.refactoring.util.JavaElementUtil;
+import org.eclipse.jdt.internal.corext.util.JavaConventionsUtil;
+import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
+import org.eclipse.jdt.internal.corext.util.JdtFlags;
+import org.eclipse.jdt.internal.corext.util.Messages;
+import org.eclipse.jdt.internal.corext.util.Resources;
+
+import org.eclipse.jdt.ui.JavaElementLabels;
+
+import org.eclipse.jdt.internal.ui.text.correction.ASTResolving;
+import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
+
+/**
+ * This class defines a set of reusable static checks methods.
+ */
+public class Checks {
+
+ /*
+ * no instances
+ */
+ private Checks(){
+ }
+
+ /* Constants returned by checkExpressionIsRValue */
+ public static final int IS_RVALUE= 0;
+ public static final int NOT_RVALUE_MISC= 1;
+ public static final int NOT_RVALUE_VOID= 2;
+
+ /**
+ * @since 3.6
+ */
+ public static final int IS_RVALUE_GUESSED= 3;
+
+ /**
+ * Checks if method will have a constructor name after renaming.
+ * @param method
+ * @param newMethodName
+ * @param newTypeName
+ * @return <code>RefactoringStatus</code> with <code>WARNING</code> severity if
+ * the give method will have a constructor name after renaming
+ * <code>null</code> otherwise.
+ */
+ public static RefactoringStatus checkIfConstructorName(IMethod method, String newMethodName, String newTypeName){
+ if (! newMethodName.equals(newTypeName))
+ return null;
+ else
+ return RefactoringStatus.createWarningStatus(
+ Messages.format(RefactoringCoreMessages.Checks_constructor_name,
+ new Object[] {JavaElementUtil.createMethodSignature(method), JavaElementLabels.getElementLabel(method.getDeclaringType(), JavaElementLabels.ALL_FULLY_QUALIFIED) } ));
+ }
+
+ /**
+ * Checks if the given name is a valid Java field name.
+ *
+ * @param name the java field name.
+ * @param context an {@link IJavaElement} or <code>null</code>
+ * @return a refactoring status containing the error message if the
+ * name is not a valid java field name.
+ */
+ public static RefactoringStatus checkFieldName(String name, IJavaElement context) {
+ return checkName(name, JavaConventionsUtil.validateFieldName(name, context));
+ }
+
+ /**
+ * Checks if the given name is a valid Java type parameter name.
+ *
+ * @param name the java type parameter name.
+ * @param context an {@link IJavaElement} or <code>null</code>
+ * @return a refactoring status containing the error message if the
+ * name is not a valid java type parameter name.
+ */
+ public static RefactoringStatus checkTypeParameterName(String name, IJavaElement context) {
+ return checkName(name, JavaConventionsUtil.validateTypeVariableName(name, context));
+ }
+
+ /**
+ * Checks if the given name is a valid Java identifier.
+ *
+ * @param name the java identifier.
+ * @param context an {@link IJavaElement} or <code>null</code>
+ * @return a refactoring status containing the error message if the
+ * name is not a valid java identifier.
+ */
+ public static RefactoringStatus checkIdentifier(String name, IJavaElement context) {
+ return checkName(name, JavaConventionsUtil.validateIdentifier(name, context));
+ }
+
+ /**
+ * Checks if the given name is a valid Java method name.
+ *
+ * @param name the java method name.
+ * @param context an {@link IJavaElement} or <code>null</code>
+ * @return a refactoring status containing the error message if the
+ * name is not a valid java method name.
+ */
+ public static RefactoringStatus checkMethodName(String name, IJavaElement context) {
+ RefactoringStatus status= checkName(name, JavaConventionsUtil.validateMethodName(name, context));
+ if (status.isOK() && !startsWithLowerCase(name))
+ return RefactoringStatus.createWarningStatus(RefactoringCoreMessages.Checks_method_names_lowercase);
+ else
+ return status;
+ }
+
+ /**
+ * Checks if the given name is a valid Java type name.
+ *
+ * @param name the java method name.
+ * @param context an {@link IJavaElement} or <code>null</code>
+ * @return a refactoring status containing the error message if the
+ * name is not a valid java type name.
+ */
+ public static RefactoringStatus checkTypeName(String name, IJavaElement context) {
+ //fix for: 1GF5Z0Z: ITPJUI:WINNT - assertion failed after renameType refactoring
+ if (name.indexOf(".") != -1) //$NON-NLS-1$
+ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.Checks_no_dot);
+ else
+ return checkName(name, JavaConventionsUtil.validateJavaTypeName(name, context));
+ }
+
+ /**
+ * Checks if the given name is a valid Java package name.
+ *
+ * @param name the java package name.
+ * @param context an {@link IJavaElement} or <code>null</code>
+ * @return a refactoring status containing the error message if the
+ * name is not a valid java package name.
+ */
+ public static RefactoringStatus checkPackageName(String name, IJavaElement context) {
+ return checkName(name, JavaConventionsUtil.validatePackageName(name, context));
+ }
+
+ /**
+ * Checks if the given name is a valid compilation unit name.
+ *
+ * @param name the compilation unit name.
+ * @param context an {@link IJavaElement} or <code>null</code>
+ * @return a refactoring status containing the error message if the
+ * name is not a valid compilation unit name.
+ */
+ public static RefactoringStatus checkCompilationUnitName(String name, IJavaElement context) {
+ return checkName(name, JavaConventionsUtil.validateCompilationUnitName(name, context));
+ }
+
+ /**
+ * Returns OK status if the new name is OK, i.e. when no file with that name exists.
+ * The name of the given CU is not OK.
+ *
+ * @param cu CU to rename
+ * @param newBareName the new name of the CU (without extension)
+ * @return the status: FATAL if the CU already exists, OK if OK
+ */
+ public static RefactoringStatus checkCompilationUnitNewName(ICompilationUnit cu, String newBareName) {
+ String newCUName= JavaModelUtil.getRenamedCUName(cu, newBareName);
+ IPath renamedResourcePath= cu.getParent().getPath().append(newCUName);
+ if (resourceExists(renamedResourcePath))
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.Checks_cu_name_used, BasicElementLabels.getResourceName(newCUName)));
+ else
+ return new RefactoringStatus();
+ }
+
+ public static boolean startsWithLowerCase(String s){
+ if (s == null)
+ return false;
+ else if ("".equals(s)) //$NON-NLS-1$
+ return false;
+ else
+ //workaround for JDK bug (see 26529)
+ return s.charAt(0) == Character.toLowerCase(s.charAt(0));
+ }
+
+ public static boolean resourceExists(IPath resourcePath){
+ return ResourcesPlugin.getWorkspace().getRoot().findMember(resourcePath) != null;
+ }
+
+ public static boolean isTopLevel(IType type){
+ return type.getDeclaringType() == null;
+ }
+
+ public static boolean isAnonymous(IType type) throws JavaModelException {
+ return type.isAnonymous();
+ }
+
+ public static boolean isTopLevelType(IMember member){
+ return member.getElementType() == IJavaElement.TYPE && isTopLevel((IType) member);
+ }
+
+ public static boolean isInsideLocalType(IType type) throws JavaModelException {
+ while (type != null) {
+ if (type.isLocal())
+ return true;
+ type= type.getDeclaringType();
+ }
+ return false;
+ }
+
+ public static boolean isAlreadyNamed(IJavaElement element, String name){
+ return name.equals(element.getElementName());
+ }
+
+ //-------------- main and native method checks ------------------
+ public static RefactoringStatus checkForMainAndNativeMethods(ICompilationUnit cu) throws JavaModelException {
+ return checkForMainAndNativeMethods(cu.getTypes());
+ }
+
+ public static RefactoringStatus checkForMainAndNativeMethods(IType[] types) throws JavaModelException {
+ RefactoringStatus result= new RefactoringStatus();
+ for (int i= 0; i < types.length; i++)
+ result.merge(checkForMainAndNativeMethods(types[i]));
+ return result;
+ }
+
+ public static RefactoringStatus checkForMainAndNativeMethods(IType type) throws JavaModelException {
+ RefactoringStatus result= new RefactoringStatus();
+ result.merge(checkForMainAndNativeMethods(type.getMethods()));
+ result.merge(checkForMainAndNativeMethods(type.getTypes()));
+ return result;
+ }
+
+ private static RefactoringStatus checkForMainAndNativeMethods(IMethod[] methods) throws JavaModelException {
+ RefactoringStatus result= new RefactoringStatus();
+ for (int i= 0; i < methods.length; i++) {
+ if (JdtFlags.isNative(methods[i])){
+ String typeName= JavaElementLabels.getElementLabel(methods[i].getDeclaringType(), JavaElementLabels.ALL_FULLY_QUALIFIED);
+ String methodName= JavaElementLabels.getElementLabel(methods[i], JavaElementLabels.ALL_DEFAULT);
+ String msg= Messages.format(RefactoringCoreMessages.Checks_method_native,
+ new String[]{typeName, methodName, "UnsatisfiedLinkError"});//$NON-NLS-1$
+ result.addEntry(RefactoringStatus.ERROR, msg, JavaStatusContext.create(methods[i]), Corext.getPluginId(), RefactoringStatusCodes.NATIVE_METHOD);
+ }
+ if (methods[i].isMainMethod()) {
+ String msg= Messages.format(RefactoringCoreMessages.Checks_has_main,
+ JavaElementLabels.getElementLabel(methods[i].getDeclaringType(), JavaElementLabels.ALL_FULLY_QUALIFIED));
+ result.addEntry(RefactoringStatus.WARNING, msg, JavaStatusContext.create(methods[i]), Corext.getPluginId(), RefactoringStatusCodes.MAIN_METHOD);
+ }
+ }
+ return result;
+ }
+
+ //---- New method name checking -------------------------------------------------------------
+
+ /**
+ * Checks if the new method is already used in the given type.
+ * @param type
+ * @param methodName
+ * @param parameters
+ * @return the status
+ */
+ public static RefactoringStatus checkMethodInType(ITypeBinding type, String methodName, ITypeBinding[] parameters) {
+ RefactoringStatus result= new RefactoringStatus();
+ if (methodName.equals(type.getName()))
+ result.addWarning(RefactoringCoreMessages.Checks_methodName_constructor);
+ IMethodBinding method= org.eclipse.jdt.internal.corext.dom.Bindings.findMethodInType(type, methodName, parameters);
+ if (method != null)
+ result.addError(Messages.format(RefactoringCoreMessages.Checks_methodName_exists,
+ new Object[] {BasicElementLabels.getJavaElementName(methodName), BasicElementLabels.getJavaElementName(type.getName())}),
+ JavaStatusContext.create(method));
+ return result;
+ }
+
+ /**
+ * Checks if the new method somehow conflicts with an already existing method in
+ * the hierarchy. The following checks are done:
+ * <ul>
+ * <li> if the new method overrides a method defined in the given type or in one of its
+ * super classes. </li>
+ * </ul>
+ * @param type
+ * @param methodName
+ * @param returnType
+ * @param parameters
+ * @return the status
+ */
+ public static RefactoringStatus checkMethodInHierarchy(ITypeBinding type, String methodName, ITypeBinding returnType, ITypeBinding[] parameters) {
+ RefactoringStatus result= new RefactoringStatus();
+ IMethodBinding method= Bindings.findMethodInHierarchy(type, methodName, parameters);
+ if (method != null) {
+ boolean returnTypeClash= false;
+ ITypeBinding methodReturnType= method.getReturnType();
+ if (returnType != null && methodReturnType != null) {
+ String returnTypeKey= returnType.getKey();
+ String methodReturnTypeKey= methodReturnType.getKey();
+ if (returnTypeKey == null && methodReturnTypeKey == null) {
+ returnTypeClash= returnType != methodReturnType;
+ } else if (returnTypeKey != null && methodReturnTypeKey != null) {
+ returnTypeClash= !returnTypeKey.equals(methodReturnTypeKey);
+ }
+ }
+ ITypeBinding dc= method.getDeclaringClass();
+ if (returnTypeClash) {
+ result.addError(Messages.format(RefactoringCoreMessages.Checks_methodName_returnTypeClash,
+ new Object[] {BasicElementLabels.getJavaElementName(methodName), BasicElementLabels.getJavaElementName(dc.getName())}),
+ JavaStatusContext.create(method));
+ } else {
+ result.addError(Messages.format(RefactoringCoreMessages.Checks_methodName_overrides,
+ new Object[] {BasicElementLabels.getJavaElementName(methodName), BasicElementLabels.getJavaElementName(dc.getName())}),
+ JavaStatusContext.create(method));
+ }
+ }
+ return result;
+ }
+
+ //---- Selection checks --------------------------------------------------------------------
+
+ public static boolean isExtractableExpression(ASTNode[] selectedNodes, ASTNode coveringNode) {
+ ASTNode node= coveringNode;
+ if (isEnumCase(node))
+ return false;
+ if (selectedNodes != null && selectedNodes.length == 1)
+ node= selectedNodes[0];
+ return isExtractableExpression(node);
+ }
+
+ public static boolean isEnumCase(ASTNode node) {
+ if (node instanceof SwitchCase) {
+ final SwitchCase caze= (SwitchCase) node;
+ final Expression expression= caze.getExpression();
+ if (expression instanceof Name) {
+ final Name name= (Name) expression;
+ final IBinding binding= name.resolveBinding();
+ if (binding instanceof IVariableBinding) {
+ IVariableBinding variableBinding= (IVariableBinding) binding;
+ return variableBinding.isEnumConstant();
+ }
+ }
+ }
+ return false;
+ }
+
+ public static boolean isExtractableExpression(ASTNode node) {
+ if (!(node instanceof Expression))
+ return false;
+ if (node instanceof Name) {
+ IBinding binding= ((Name) node).resolveBinding();
+ return binding == null || binding instanceof IVariableBinding;
+ }
+ return true;
+ }
+
+ public static boolean isInsideJavadoc(ASTNode node) {
+ do {
+ if (node.getNodeType() == ASTNode.JAVADOC)
+ return true;
+ node= node.getParent();
+ } while (node != null);
+ return false;
+ }
+
+ /**
+ * Returns a fatal error in case the name is empty. In all other cases, an
+ * error based on the given status is returned.
+ *
+ * @param name a name
+ * @param status a status
+ * @return RefactoringStatus based on the given status or the name, if
+ * empty.
+ */
+ public static RefactoringStatus checkName(String name, IStatus status) {
+ RefactoringStatus result= new RefactoringStatus();
+ if ("".equals(name)) //$NON-NLS-1$
+ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.Checks_Choose_name);
+
+ if (status.isOK())
+ return result;
+
+ switch (status.getSeverity()){
+ case IStatus.ERROR:
+ return RefactoringStatus.createFatalErrorStatus(status.getMessage());
+ case IStatus.WARNING:
+ return RefactoringStatus.createWarningStatus(status.getMessage());
+ case IStatus.INFO:
+ return RefactoringStatus.createInfoStatus(status.getMessage());
+ default: //no nothing
+ return new RefactoringStatus();
+ }
+ }
+
+ /**
+ * Finds a method in a type
+ * This searches for a method with the same name and signature. Parameter types are only
+ * compared by the simple name, no resolving for the fully qualified type name is done
+ * @param name
+ * @param parameterCount
+ * @param isConstructor
+ * @param type
+ * @return The first found method or null, if nothing found
+ * @throws JavaModelException
+ */
+ public static IMethod findMethod(String name, int parameterCount, boolean isConstructor, IType type) throws JavaModelException {
+ return findMethod(name, parameterCount, isConstructor, type.getMethods());
+ }
+
+ /**
+ * Finds a method in a type.
+ * Searches for a method with the same name and the same parameter count.
+ * Parameter types are <b>not</b> compared.
+ * @param method
+ * @param type
+ * @return The first found method or null, if nothing found
+ * @throws JavaModelException
+ */
+ public static IMethod findMethod(IMethod method, IType type) throws JavaModelException {
+ return findMethod(method.getElementName(), method.getParameterTypes().length, method.isConstructor(), type.getMethods());
+ }
+
+ /**
+ * Finds a method in an array of methods.
+ * Searches for a method with the same name and the same parameter count.
+ * Parameter types are <b>not</b> compared.
+ * @param method
+ * @param methods
+ * @return The first found method or null, if nothing found
+ * @throws JavaModelException
+ */
+ public static IMethod findMethod(IMethod method, IMethod[] methods) throws JavaModelException {
+ return findMethod(method.getElementName(), method.getParameterTypes().length, method.isConstructor(), methods);
+ }
+
+ public static IMethod findMethod(String name, int parameters, boolean isConstructor, IMethod[] methods) throws JavaModelException {
+ for (int i= methods.length-1; i >= 0; i--) {
+ IMethod curr= methods[i];
+ if (name.equals(curr.getElementName())) {
+ if (isConstructor == curr.isConstructor()) {
+ if (parameters == curr.getParameterTypes().length) {
+ return curr;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Finds a method in a type.
+ * This searches for a method with the same name and signature. Parameter types are only
+ * compared by the simple name, no resolving for the fully qualified type name is done
+ * @param method
+ * @param type
+ * @return The first found method or null, if nothing found
+ * @throws JavaModelException
+ */
+ public static IMethod findSimilarMethod(IMethod method, IType type) throws JavaModelException {
+ return findSimilarMethod(method, type.getMethods());
+ }
+
+ /**
+ * Finds a method in an array of methods.
+ * This searches for a method with the same name and signature. Parameter types are only
+ * compared by the simple name, no resolving for the fully qualified type name is done
+ * @param method
+ * @param methods
+ * @return The first found method or null, if nothing found
+ * @throws JavaModelException
+ */
+ public static IMethod findSimilarMethod(IMethod method, IMethod[] methods) throws JavaModelException {
+ boolean isConstructor= method.isConstructor();
+ for (int i= 0; i < methods.length; i++) {
+ IMethod otherMethod= methods[i];
+ if (otherMethod.isConstructor() == isConstructor && method.isSimilar(otherMethod))
+ return otherMethod;
+ }
+ return null;
+ }
+
+ /*
+ * Compare two parameter signatures
+ */
+ public static boolean compareParamTypes(String[] paramTypes1, String[] paramTypes2) {
+ if (paramTypes1.length == paramTypes2.length) {
+ int i= 0;
+ while (i < paramTypes1.length) {
+ String t1= Signature.getSimpleName(Signature.toString(paramTypes1[i]));
+ String t2= Signature.getSimpleName(Signature.toString(paramTypes2[i]));
+ if (!t1.equals(t2)) {
+ return false;
+ }
+ i++;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ //---------------------
+
+ public static RefactoringStatus checkIfCuBroken(IMember member) throws JavaModelException{
+ ICompilationUnit cu= (ICompilationUnit)JavaCore.create(member.getCompilationUnit().getResource());
+ if (cu == null)
+ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.Checks_cu_not_created);
+ else if (! cu.isStructureKnown())
+ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.Checks_cu_not_parsed);
+ return new RefactoringStatus();
+ }
+
+ /**
+ * From SearchResultGroup[] passed as the parameter
+ * this method removes all those that correspond to a non-parsable ICompilationUnit
+ * and returns it as a result.
+ * @param grouped the array of search result groups from which non parsable compilation
+ * units are to be removed.
+ * @param status a refactoring status to collect errors and problems
+ * @return the array of search result groups
+ * @throws JavaModelException
+ */
+ public static SearchResultGroup[] excludeCompilationUnits(SearchResultGroup[] grouped, RefactoringStatus status) throws JavaModelException{
+ List<SearchResultGroup> result= new ArrayList<SearchResultGroup>();
+ boolean wasEmpty= grouped.length == 0;
+ for (int i= 0; i < grouped.length; i++){
+ IResource resource= grouped[i].getResource();
+ IJavaElement element= JavaCore.create(resource);
+ if (! (element instanceof ICompilationUnit))
+ continue;
+ //XXX this is a workaround for a jcore feature that shows errors in cus only when you get the original element
+ ICompilationUnit cu= (ICompilationUnit)JavaCore.create(resource);
+ if (! cu.isStructureKnown()){
+ status.addError(Messages.format(RefactoringCoreMessages.Checks_cannot_be_parsed, BasicElementLabels.getPathLabel(cu.getPath(), false)));
+ continue; //removed, go to the next one
+ }
+ result.add(grouped[i]);
+ }
+
+ if ((!wasEmpty) && result.isEmpty())
+ status.addFatalError(RefactoringCoreMessages.Checks_all_excluded);
+
+ return result.toArray(new SearchResultGroup[result.size()]);
+ }
+
+ public static RefactoringStatus checkCompileErrorsInAffectedFiles(SearchResultGroup[] grouped) throws JavaModelException {
+ RefactoringStatus result= new RefactoringStatus();
+ for (int i= 0; i < grouped.length; i++)
+ checkCompileErrorsInAffectedFile(result, grouped[i].getResource());
+ return result;
+ }
+
+ public static void checkCompileErrorsInAffectedFile(RefactoringStatus result, IResource resource) throws JavaModelException {
+ if (hasCompileErrors(resource))
+ result.addWarning(Messages.format(RefactoringCoreMessages.Checks_cu_has_compile_errors, BasicElementLabels.getPathLabel(resource.getFullPath(), false)));
+ }
+
+ public static RefactoringStatus checkCompileErrorsInAffectedFiles(SearchResultGroup[] references, IResource declaring) throws JavaModelException {
+ RefactoringStatus result= new RefactoringStatus();
+ for (int i= 0; i < references.length; i++){
+ IResource resource= references[i].getResource();
+ if (resource.equals(declaring))
+ declaring= null;
+ checkCompileErrorsInAffectedFile(result, resource);
+ }
+ if (declaring != null)
+ checkCompileErrorsInAffectedFile(result, declaring);
+ return result;
+ }
+
+ private static boolean hasCompileErrors(IResource resource) throws JavaModelException {
+ try {
+ IMarker[] problemMarkers= resource.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE);
+ for (int i= 0; i < problemMarkers.length; i++) {
+ if (problemMarkers[i].getAttribute(IMarker.SEVERITY, -1) == IMarker.SEVERITY_ERROR)
+ return true;
+ }
+ return false;
+ } catch (JavaModelException e){
+ throw e;
+ } catch (CoreException e){
+ throw new JavaModelException(e);
+ }
+ }
+
+ //------
+ public static boolean isReadOnly(Object element) throws JavaModelException{
+ if (element instanceof IResource)
+ return isReadOnly((IResource)element);
+
+ if (element instanceof IJavaElement) {
+ if ((element instanceof IPackageFragmentRoot) && isClasspathDelete((IPackageFragmentRoot)element))
+ return false;
+ return isReadOnly(((IJavaElement)element).getResource());
+ }
+
+ Assert.isTrue(false, "not expected to get here"); //$NON-NLS-1$
+ return false;
+ }
+
+ public static boolean isReadOnly(IResource res) throws JavaModelException {
+ ResourceAttributes attributes= res.getResourceAttributes();
+ if (attributes != null && attributes.isReadOnly())
+ return true;
+
+ if (! (res instanceof IContainer))
+ return false;
+
+ IContainer container= (IContainer)res;
+ try {
+ IResource[] children= container.members();
+ for (int i= 0; i < children.length; i++) {
+ if (isReadOnly(children[i]))
+ return true;
+ }
+ return false;
+ } catch (JavaModelException e){
+ throw e;
+ } catch (CoreException e) {
+ throw new JavaModelException(e);
+ }
+ }
+
+ public static boolean isClasspathDelete(IPackageFragmentRoot pkgRoot) {
+ IResource res= pkgRoot.getResource();
+ if (res == null)
+ return true;
+ IProject definingProject= res.getProject();
+ if (res.getParent() != null && pkgRoot.isArchive() && ! res.getParent().equals(definingProject))
+ return true;
+
+ IProject occurringProject= pkgRoot.getJavaProject().getProject();
+ return !definingProject.equals(occurringProject);
+ }
+
+ //-------- validateEdit checks ----
+
+ public static RefactoringStatus validateModifiesFiles(IFile[] filesToModify, Object context) {
+ RefactoringStatus result= new RefactoringStatus();
+ IStatus status= Resources.checkInSync(filesToModify);
+ if (!status.isOK())
+ result.merge(RefactoringStatus.create(status));
+ status= Resources.makeCommittable(filesToModify, context);
+ if (!status.isOK()) {
+ result.merge(RefactoringStatus.create(status));
+ if (!result.hasFatalError()) {
+ result.addFatalError(RefactoringCoreMessages.Checks_validateEdit);
+ }
+ }
+ return result;
+ }
+
+ public static void addModifiedFilesToChecker(IFile[] filesToModify, CheckConditionsContext context) {
+ ResourceChangeChecker checker= (ResourceChangeChecker) context.getChecker(ResourceChangeChecker.class);
+ IResourceChangeDescriptionFactory deltaFactory= checker.getDeltaFactory();
+
+ for (int i= 0; i < filesToModify.length; i++) {
+ deltaFactory.change(filesToModify[i]);
+ }
+ }
+
+
+ public static RefactoringStatus validateEdit(ICompilationUnit unit, Object context) {
+ IResource resource= unit.getPrimary().getResource();
+ RefactoringStatus result= new RefactoringStatus();
+ if (resource == null)
+ return result;
+ IStatus status= Resources.checkInSync(resource);
+ if (!status.isOK())
+ result.merge(RefactoringStatus.create(status));
+ status= Resources.makeCommittable(resource, context);
+ if (!status.isOK()) {
+ result.merge(RefactoringStatus.create(status));
+ if (!result.hasFatalError()) {
+ result.addFatalError(RefactoringCoreMessages.Checks_validateEdit);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Checks whether it is possible to modify the given <code>IJavaElement</code>.
+ * The <code>IJavaElement</code> must exist and be non read-only to be modifiable.
+ * Moreover, if it is a <code>IMember</code> it must not be binary.
+ * The returned <code>RefactoringStatus</code> has <code>ERROR</code> severity if
+ * it is not possible to modify the element.
+ * @param javaElement
+ * @return the status
+ * @throws JavaModelException
+ *
+ * @see IJavaElement#exists
+ * @see IJavaElement#isReadOnly
+ * @see IMember#isBinary
+ * @see RefactoringStatus
+ */
+ public static RefactoringStatus checkAvailability(IJavaElement javaElement) throws JavaModelException{
+ RefactoringStatus result= new RefactoringStatus();
+ if (! javaElement.exists())
+ result.addFatalError(Messages.format(RefactoringCoreMessages.Refactoring_not_in_model, getJavaElementName(javaElement)));
+ if (javaElement.isReadOnly())
+ result.addFatalError(Messages.format(RefactoringCoreMessages.Refactoring_read_only, getJavaElementName(javaElement)));
+ if (javaElement.exists() && !javaElement.isStructureKnown())
+ result.addFatalError(Messages.format(RefactoringCoreMessages.Refactoring_unknown_structure, getJavaElementName(javaElement)));
+ if (javaElement instanceof IMember && ((IMember)javaElement).isBinary())
+ result.addFatalError(Messages.format(RefactoringCoreMessages.Refactoring_binary, getJavaElementName(javaElement)));
+ return result;
+ }
+
+ private static String getJavaElementName(IJavaElement element) {
+ return JavaElementLabels.getElementLabel(element, JavaElementLabels.ALL_DEFAULT);
+ }
+
+ public static boolean isAvailable(IJavaElement javaElement) throws JavaModelException {
+ if (javaElement == null)
+ return false;
+ if (! javaElement.exists())
+ return false;
+ if (javaElement.isReadOnly())
+ return false;
+ // work around for https://bugs.eclipse.org/bugs/show_bug.cgi?id=48422
+ // the Java project is now cheating regarding its children so we shouldn't
+ // call isStructureKnown if the project isn't open.
+ // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=52474
+ if (!(javaElement instanceof IJavaProject) && !(javaElement instanceof ILocalVariable) && !javaElement.isStructureKnown())
+ return false;
+ if (javaElement instanceof IMember && ((IMember)javaElement).isBinary())
+ return false;
+ return true;
+ }
+
+ public static IType findTypeInPackage(IPackageFragment pack, String elementName) throws JavaModelException {
+ Assert.isTrue(pack.exists());
+ Assert.isTrue(!pack.isReadOnly());
+
+ String packageName= pack.getElementName();
+ elementName= packageName.length() > 0 ? packageName + '.' + elementName : elementName;
+
+ return pack.getJavaProject().findType(elementName, (IProgressMonitor) null);
+ }
+
+ public static RefactoringStatus checkTempName(String newName, IJavaElement context) {
+ RefactoringStatus result= Checks.checkIdentifier(newName, context);
+ if (result.hasFatalError())
+ return result;
+ if (! Checks.startsWithLowerCase(newName))
+ result.addWarning(RefactoringCoreMessages.ExtractTempRefactoring_convention);
+ return result;
+ }
+
+ public static RefactoringStatus checkEnumConstantName(String newName, IJavaElement context) {
+ RefactoringStatus result= Checks.checkFieldName(newName, context);
+ if (result.hasFatalError())
+ return result;
+ for (int i= 0; i < newName.length(); i++) {
+ char c= newName.charAt(i);
+ if (Character.isLetter(c) && !Character.isUpperCase(c)) {
+ result.addWarning(RefactoringCoreMessages.RenameEnumConstRefactoring_convention);
+ break;
+ }
+ }
+ return result;
+ }
+
+ public static RefactoringStatus checkConstantName(String newName, IJavaElement context) {
+ RefactoringStatus result= Checks.checkFieldName(newName, context);
+ if (result.hasFatalError())
+ return result;
+ for (int i= 0; i < newName.length(); i++) {
+ char c= newName.charAt(i);
+ if (Character.isLetter(c) && !Character.isUpperCase(c)) {
+ result.addWarning(RefactoringCoreMessages.ExtractConstantRefactoring_convention);
+ break;
+ }
+ }
+ return result;
+ }
+
+ public static boolean isException(IType iType, IProgressMonitor pm) throws JavaModelException {
+ try{
+ if (! iType.isClass())
+ return false;
+ IType[] superTypes= iType.newSupertypeHierarchy(pm).getAllSupertypes(iType);
+ for (int i= 0; i < superTypes.length; i++) {
+ if ("java.lang.Throwable".equals(superTypes[i].getFullyQualifiedName())) //$NON-NLS-1$
+ return true;
+ }
+ return false;
+ } finally{
+ pm.done();
+ }
+ }
+
+ /**
+ * @param e
+ * @return int
+ * Checks.IS_RVALUE if e is an rvalue
+ * Checks.IS_RVALUE_GUESSED if e is guessed as an rvalue
+ * Checks.NOT_RVALUE_VOID if e is not an rvalue because its type is void
+ * Checks.NOT_RVALUE_MISC if e is not an rvalue for some other reason
+ */
+ public static int checkExpressionIsRValue(Expression e) {
+ if (e instanceof Name) {
+ if(!(((Name) e).resolveBinding() instanceof IVariableBinding)) {
+ return NOT_RVALUE_MISC;
+ }
+ }
+ if (e instanceof Annotation)
+ return NOT_RVALUE_MISC;
+
+
+ ITypeBinding tb= e.resolveTypeBinding();
+ boolean guessingRequired= false;
+ if (tb == null) {
+ guessingRequired= true;
+ tb= ASTResolving.guessBindingForReference(e);
+ }
+ if (tb == null)
+ return NOT_RVALUE_MISC;
+ else if (tb.getName().equals("void")) //$NON-NLS-1$
+ return NOT_RVALUE_VOID;
+
+ return guessingRequired ? IS_RVALUE_GUESSED : IS_RVALUE;
+ }
+
+ public static boolean isDeclaredIn(VariableDeclaration tempDeclaration, Class<? extends ASTNode> astNodeClass) {
+ ASTNode initializer= ASTNodes.getParent(tempDeclaration, astNodeClass);
+ if (initializer == null)
+ return false;
+ ASTNode anonymous= ASTNodes.getParent(tempDeclaration, AnonymousClassDeclaration.class);
+ if (anonymous == null)
+ return true;
+ // stupid code. Is to find out if the variable declaration isn't a field.
+ if (ASTNodes.isParent(anonymous, initializer))
+ return false;
+ return true;
+ }
+}