0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2006 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.jdt.internal.corext.refactoring;
0011:
0012: import java.util.ArrayList;
0013: import java.util.List;
0014:
0015: import org.eclipse.core.runtime.Assert;
0016: import org.eclipse.core.runtime.CoreException;
0017: import org.eclipse.core.runtime.IPath;
0018: import org.eclipse.core.runtime.IProgressMonitor;
0019: import org.eclipse.core.runtime.IStatus;
0020:
0021: import org.eclipse.core.resources.IContainer;
0022: import org.eclipse.core.resources.IFile;
0023: import org.eclipse.core.resources.IMarker;
0024: import org.eclipse.core.resources.IProject;
0025: import org.eclipse.core.resources.IResource;
0026: import org.eclipse.core.resources.ResourceAttributes;
0027: import org.eclipse.core.resources.ResourcesPlugin;
0028:
0029: import org.eclipse.ltk.core.refactoring.RefactoringStatus;
0030:
0031: import org.eclipse.jdt.core.ICompilationUnit;
0032: import org.eclipse.jdt.core.IJavaElement;
0033: import org.eclipse.jdt.core.IJavaModelMarker;
0034: import org.eclipse.jdt.core.IJavaProject;
0035: import org.eclipse.jdt.core.ILocalVariable;
0036: import org.eclipse.jdt.core.IMember;
0037: import org.eclipse.jdt.core.IMethod;
0038: import org.eclipse.jdt.core.IPackageFragment;
0039: import org.eclipse.jdt.core.IPackageFragmentRoot;
0040: import org.eclipse.jdt.core.IType;
0041: import org.eclipse.jdt.core.JavaCore;
0042: import org.eclipse.jdt.core.JavaModelException;
0043: import org.eclipse.jdt.core.Signature;
0044: import org.eclipse.jdt.core.dom.ASTNode;
0045: import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
0046: import org.eclipse.jdt.core.dom.Expression;
0047: import org.eclipse.jdt.core.dom.IBinding;
0048: import org.eclipse.jdt.core.dom.IMethodBinding;
0049: import org.eclipse.jdt.core.dom.ITypeBinding;
0050: import org.eclipse.jdt.core.dom.IVariableBinding;
0051: import org.eclipse.jdt.core.dom.Name;
0052: import org.eclipse.jdt.core.dom.SwitchCase;
0053: import org.eclipse.jdt.core.dom.VariableDeclaration;
0054:
0055: import org.eclipse.jdt.internal.corext.Corext;
0056: import org.eclipse.jdt.internal.corext.dom.ASTNodes;
0057: import org.eclipse.jdt.internal.corext.dom.Bindings;
0058: import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext;
0059: import org.eclipse.jdt.internal.corext.refactoring.base.RefactoringStatusCodes;
0060: import org.eclipse.jdt.internal.corext.refactoring.changes.RenameResourceChange;
0061: import org.eclipse.jdt.internal.corext.refactoring.util.JavaElementUtil;
0062: import org.eclipse.jdt.internal.corext.util.JavaConventionsUtil;
0063: import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
0064: import org.eclipse.jdt.internal.corext.util.JdtFlags;
0065: import org.eclipse.jdt.internal.corext.util.Messages;
0066: import org.eclipse.jdt.internal.corext.util.Resources;
0067:
0068: /**
0069: * This class defines a set of reusable static checks methods.
0070: */
0071: public class Checks {
0072:
0073: /*
0074: * no instances
0075: */
0076: private Checks() {
0077: }
0078:
0079: /* Constants returned by checkExpressionIsRValue */
0080: public static final int IS_RVALUE = 0;
0081: public static final int NOT_RVALUE_MISC = 1;
0082: public static final int NOT_RVALUE_VOID = 2;
0083:
0084: /**
0085: * Checks if method will have a constructor name after renaming.
0086: * @param method
0087: * @param newMethodName
0088: * @param newTypeName
0089: * @return <code>RefactoringStatus</code> with <code>WARNING</code> severity if
0090: * the give method will have a constructor name after renaming
0091: * <code>null</code> otherwise.
0092: */
0093: public static RefactoringStatus checkIfConstructorName(
0094: IMethod method, String newMethodName, String newTypeName) {
0095: if (!newMethodName.equals(newTypeName))
0096: return null;
0097: else
0098: return RefactoringStatus
0099: .createWarningStatus(Messages
0100: .format(
0101: RefactoringCoreMessages.Checks_constructor_name,
0102: new Object[] {
0103: JavaElementUtil
0104: .createMethodSignature(method),
0105: JavaModelUtil
0106: .getFullyQualifiedName(method
0107: .getDeclaringType()) }));
0108: }
0109:
0110: /**
0111: * Checks if the given name is a valid Java field name.
0112: *
0113: * @param name the java field name.
0114: * @param context an {@link IJavaElement} or <code>null</code>
0115: * @return a refactoring status containing the error message if the
0116: * name is not a valid java field name.
0117: */
0118: public static RefactoringStatus checkFieldName(String name,
0119: IJavaElement context) {
0120: return checkName(name, JavaConventionsUtil.validateFieldName(
0121: name, context));
0122: }
0123:
0124: /**
0125: * Checks if the given name is a valid Java type parameter name.
0126: *
0127: * @param name the java type parameter name.
0128: * @param context an {@link IJavaElement} or <code>null</code>
0129: * @return a refactoring status containing the error message if the
0130: * name is not a valid java type parameter name.
0131: */
0132: public static RefactoringStatus checkTypeParameterName(String name,
0133: IJavaElement context) {
0134: return checkName(name, JavaConventionsUtil
0135: .validateTypeVariableName(name, context));
0136: }
0137:
0138: /**
0139: * Checks if the given name is a valid Java identifier.
0140: *
0141: * @param name the java identifier.
0142: * @param context an {@link IJavaElement} or <code>null</code>
0143: * @return a refactoring status containing the error message if the
0144: * name is not a valid java identifier.
0145: */
0146: public static RefactoringStatus checkIdentifier(String name,
0147: IJavaElement context) {
0148: return checkName(name, JavaConventionsUtil.validateIdentifier(
0149: name, context));
0150: }
0151:
0152: /**
0153: * Checks if the given name is a valid Java method name.
0154: *
0155: * @param name the java method name.
0156: * @param context an {@link IJavaElement} or <code>null</code>
0157: * @return a refactoring status containing the error message if the
0158: * name is not a valid java method name.
0159: */
0160: public static RefactoringStatus checkMethodName(String name,
0161: IJavaElement context) {
0162: RefactoringStatus status = checkName(name, JavaConventionsUtil
0163: .validateMethodName(name, context));
0164: if (status.isOK() && startsWithUpperCase(name))
0165: return RefactoringStatus
0166: .createWarningStatus(RefactoringCoreMessages.Checks_method_names_lowercase);
0167: else
0168: return status;
0169: }
0170:
0171: /**
0172: * Checks if the given name is a valid Java type name.
0173: *
0174: * @param name the java method name.
0175: * @param context an {@link IJavaElement} or <code>null</code>
0176: * @return a refactoring status containing the error message if the
0177: * name is not a valid java type name.
0178: */
0179: public static RefactoringStatus checkTypeName(String name,
0180: IJavaElement context) {
0181: //fix for: 1GF5Z0Z: ITPJUI:WINNT - assertion failed after renameType refactoring
0182: if (name.indexOf(".") != -1) //$NON-NLS-1$
0183: return RefactoringStatus
0184: .createFatalErrorStatus(RefactoringCoreMessages.Checks_no_dot);
0185: else
0186: return checkName(name, JavaConventionsUtil
0187: .validateJavaTypeName(name, context));
0188: }
0189:
0190: /**
0191: * Checks if the given name is a valid Java package name.
0192: *
0193: * @param name the java package name.
0194: * @param context an {@link IJavaElement} or <code>null</code>
0195: * @return a refactoring status containing the error message if the
0196: * name is not a valid java package name.
0197: */
0198: public static RefactoringStatus checkPackageName(String name,
0199: IJavaElement context) {
0200: return checkName(name, JavaConventionsUtil.validatePackageName(
0201: name, context));
0202: }
0203:
0204: /**
0205: * Checks if the given name is a valid compilation unit name.
0206: *
0207: * @param name the compilation unit name.
0208: * @param context an {@link IJavaElement} or <code>null</code>
0209: * @return a refactoring status containing the error message if the
0210: * name is not a valid compilation unit name.
0211: */
0212: public static RefactoringStatus checkCompilationUnitName(
0213: String name, IJavaElement context) {
0214: return checkName(name, JavaConventionsUtil
0215: .validateCompilationUnitName(name, context));
0216: }
0217:
0218: /**
0219: * Returns ok status if the new name is ok. This is when no other file with that name exists.
0220: * @param cu
0221: * @param newName
0222: * @return the status
0223: */
0224: public static RefactoringStatus checkCompilationUnitNewName(
0225: ICompilationUnit cu, String newName) {
0226: String newCUName = JavaModelUtil.getRenamedCUName(cu, newName);
0227: if (resourceExists(RenameResourceChange.renamedResourcePath(cu
0228: .getResource().getFullPath(), newCUName)))
0229: return RefactoringStatus
0230: .createFatalErrorStatus(Messages
0231: .format(
0232: RefactoringCoreMessages.Checks_cu_name_used,
0233: newName));
0234: else
0235: return new RefactoringStatus();
0236: }
0237:
0238: public static boolean startsWithUpperCase(String s) {
0239: if (s == null)
0240: return false;
0241: else if ("".equals(s)) //$NON-NLS-1$
0242: return false;
0243: else
0244: //workaround for JDK bug (see 26529)
0245: return s.charAt(0) == Character.toUpperCase(s.charAt(0));
0246: }
0247:
0248: public static boolean startsWithLowerCase(String s) {
0249: if (s == null)
0250: return false;
0251: else if ("".equals(s)) //$NON-NLS-1$
0252: return false;
0253: else
0254: //workaround for JDK bug (see 26529)
0255: return s.charAt(0) == Character.toLowerCase(s.charAt(0));
0256: }
0257:
0258: public static boolean resourceExists(IPath resourcePath) {
0259: return ResourcesPlugin.getWorkspace().getRoot().findMember(
0260: resourcePath) != null;
0261: }
0262:
0263: public static boolean isTopLevel(IType type) {
0264: return type.getDeclaringType() == null;
0265: }
0266:
0267: public static boolean isAnonymous(IType type)
0268: throws JavaModelException {
0269: return type.isAnonymous();
0270: }
0271:
0272: public static boolean isTopLevelType(IMember member) {
0273: return member.getElementType() == IJavaElement.TYPE
0274: && isTopLevel((IType) member);
0275: }
0276:
0277: public static boolean isInsideLocalType(IType type)
0278: throws JavaModelException {
0279: while (type != null) {
0280: if (type.isLocal())
0281: return true;
0282: type = type.getDeclaringType();
0283: }
0284: return false;
0285: }
0286:
0287: public static boolean isAlreadyNamed(IJavaElement element,
0288: String name) {
0289: return name.equals(element.getElementName());
0290: }
0291:
0292: //-------------- main and native method checks ------------------
0293: public static RefactoringStatus checkForMainAndNativeMethods(
0294: ICompilationUnit cu) throws JavaModelException {
0295: return checkForMainAndNativeMethods(cu.getTypes());
0296: }
0297:
0298: public static RefactoringStatus checkForMainAndNativeMethods(
0299: IType[] types) throws JavaModelException {
0300: RefactoringStatus result = new RefactoringStatus();
0301: for (int i = 0; i < types.length; i++)
0302: result.merge(checkForMainAndNativeMethods(types[i]));
0303: return result;
0304: }
0305:
0306: public static RefactoringStatus checkForMainAndNativeMethods(
0307: IType type) throws JavaModelException {
0308: RefactoringStatus result = new RefactoringStatus();
0309: result.merge(checkForMainAndNativeMethods(type.getMethods()));
0310: result.merge(checkForMainAndNativeMethods(type.getTypes()));
0311: return result;
0312: }
0313:
0314: private static RefactoringStatus checkForMainAndNativeMethods(
0315: IMethod[] methods) throws JavaModelException {
0316: RefactoringStatus result = new RefactoringStatus();
0317: for (int i = 0; i < methods.length; i++) {
0318: if (JdtFlags.isNative(methods[i])) {
0319: String msg = Messages
0320: .format(
0321: RefactoringCoreMessages.Checks_method_native,
0322: new String[] {
0323: JavaModelUtil
0324: .getFullyQualifiedName(methods[i]
0325: .getDeclaringType()),
0326: methods[i].getElementName(),
0327: "UnsatisfiedLinkError" });//$NON-NLS-1$
0328: result.addEntry(RefactoringStatus.ERROR, msg,
0329: JavaStatusContext.create(methods[i]), Corext
0330: .getPluginId(),
0331: RefactoringStatusCodes.NATIVE_METHOD);
0332: }
0333: if (methods[i].isMainMethod()) {
0334: String msg = Messages.format(
0335: RefactoringCoreMessages.Checks_has_main,
0336: JavaModelUtil.getFullyQualifiedName(methods[i]
0337: .getDeclaringType()));
0338: result.addEntry(RefactoringStatus.WARNING, msg,
0339: JavaStatusContext.create(methods[i]), Corext
0340: .getPluginId(),
0341: RefactoringStatusCodes.MAIN_METHOD);
0342: }
0343: }
0344: return result;
0345: }
0346:
0347: //---- New method name checking -------------------------------------------------------------
0348:
0349: /**
0350: * Checks if the new method is already used in the given type.
0351: * @param type
0352: * @param methodName
0353: * @param parameters
0354: * @return the status
0355: */
0356: public static RefactoringStatus checkMethodInType(
0357: ITypeBinding type, String methodName,
0358: ITypeBinding[] parameters) {
0359: RefactoringStatus result = new RefactoringStatus();
0360: if (methodName.equals(type.getName()))
0361: result
0362: .addWarning(RefactoringCoreMessages.Checks_methodName_constructor);
0363: IMethodBinding method = org.eclipse.jdt.internal.corext.dom.Bindings
0364: .findMethodInType(type, methodName, parameters);
0365: if (method != null)
0366: result.addError(Messages.format(
0367: RefactoringCoreMessages.Checks_methodName_exists,
0368: new Object[] { methodName, type.getName() }),
0369: JavaStatusContext.create(method));
0370: return result;
0371: }
0372:
0373: /**
0374: * Checks if the new method somehow conflicts with an already existing method in
0375: * the hierarchy. The following checks are done:
0376: * <ul>
0377: * <li> if the new method overrides a method defined in the given type or in one of its
0378: * super classes. </li>
0379: * </ul>
0380: * @param type
0381: * @param methodName
0382: * @param returnType
0383: * @param parameters
0384: * @return the status
0385: */
0386: public static RefactoringStatus checkMethodInHierarchy(
0387: ITypeBinding type, String methodName,
0388: ITypeBinding returnType, ITypeBinding[] parameters) {
0389: RefactoringStatus result = new RefactoringStatus();
0390: IMethodBinding method = Bindings.findMethodInHierarchy(type,
0391: methodName, parameters);
0392: if (method != null) {
0393: boolean returnTypeClash = false;
0394: ITypeBinding methodReturnType = method.getReturnType();
0395: if (returnType != null && methodReturnType != null) {
0396: String returnTypeKey = returnType.getKey();
0397: String methodReturnTypeKey = methodReturnType.getKey();
0398: if (returnTypeKey == null
0399: && methodReturnTypeKey == null) {
0400: returnTypeClash = returnType != methodReturnType;
0401: } else if (returnTypeKey != null
0402: && methodReturnTypeKey != null) {
0403: returnTypeClash = !returnTypeKey
0404: .equals(methodReturnTypeKey);
0405: }
0406: }
0407: ITypeBinding dc = method.getDeclaringClass();
0408: if (returnTypeClash) {
0409: result
0410: .addError(
0411: Messages
0412: .format(
0413: RefactoringCoreMessages.Checks_methodName_returnTypeClash,
0414: new Object[] {
0415: methodName,
0416: dc.getName() }),
0417: JavaStatusContext.create(method));
0418: } else {
0419: result
0420: .addError(
0421: Messages
0422: .format(
0423: RefactoringCoreMessages.Checks_methodName_overrides,
0424: new Object[] {
0425: methodName,
0426: dc.getName() }),
0427: JavaStatusContext.create(method));
0428: }
0429: }
0430: return result;
0431: }
0432:
0433: //---- Selection checks --------------------------------------------------------------------
0434:
0435: public static boolean isExtractableExpression(
0436: ASTNode[] selectedNodes, ASTNode coveringNode) {
0437: ASTNode node = coveringNode;
0438: if (isEnumCase(node))
0439: return false;
0440: if (selectedNodes != null && selectedNodes.length == 1)
0441: node = selectedNodes[0];
0442: return isExtractableExpression(node);
0443: }
0444:
0445: public static boolean isEnumCase(ASTNode node) {
0446: if (node instanceof SwitchCase) {
0447: final SwitchCase caze = (SwitchCase) node;
0448: final Expression expression = caze.getExpression();
0449: if (expression instanceof Name) {
0450: final Name name = (Name) expression;
0451: final IBinding binding = name.resolveBinding();
0452: if (binding instanceof IVariableBinding) {
0453: IVariableBinding variableBinding = (IVariableBinding) binding;
0454: return variableBinding.isEnumConstant();
0455: }
0456: }
0457: }
0458: return false;
0459: }
0460:
0461: public static boolean isExtractableExpression(ASTNode node) {
0462: if (!(node instanceof Expression))
0463: return false;
0464: if (node instanceof Name) {
0465: IBinding binding = ((Name) node).resolveBinding();
0466: return !(binding instanceof ITypeBinding);
0467: }
0468: return true;
0469: }
0470:
0471: public static boolean isInsideJavadoc(ASTNode node) {
0472: do {
0473: if (node.getNodeType() == ASTNode.JAVADOC)
0474: return true;
0475: node = node.getParent();
0476: } while (node != null);
0477: return false;
0478: }
0479:
0480: /**
0481: * Returns a fatal error in case the name is empty. In all other cases, an
0482: * error based on the given status is returned.
0483: *
0484: * @param name a name
0485: * @param status a status
0486: * @return RefactoringStatus based on the given status or the name, if
0487: * empty.
0488: */
0489: public static RefactoringStatus checkName(String name,
0490: IStatus status) {
0491: RefactoringStatus result = new RefactoringStatus();
0492: if ("".equals(name)) //$NON-NLS-1$
0493: return RefactoringStatus
0494: .createFatalErrorStatus(RefactoringCoreMessages.Checks_Choose_name);
0495:
0496: if (status.isOK())
0497: return result;
0498:
0499: switch (status.getSeverity()) {
0500: case IStatus.ERROR:
0501: return RefactoringStatus.createFatalErrorStatus(status
0502: .getMessage());
0503: case IStatus.WARNING:
0504: return RefactoringStatus.createWarningStatus(status
0505: .getMessage());
0506: case IStatus.INFO:
0507: return RefactoringStatus.createInfoStatus(status
0508: .getMessage());
0509: default: //no nothing
0510: return new RefactoringStatus();
0511: }
0512: }
0513:
0514: /**
0515: * Finds a method in a type
0516: * This searches for a method with the same name and signature. Parameter types are only
0517: * compared by the simple name, no resolving for the fully qualified type name is done
0518: * @param name
0519: * @param parameterCount
0520: * @param isConstructor
0521: * @param type
0522: * @return The first found method or null, if nothing found
0523: * @throws JavaModelException
0524: */
0525: public static IMethod findMethod(String name, int parameterCount,
0526: boolean isConstructor, IType type)
0527: throws JavaModelException {
0528: return findMethod(name, parameterCount, isConstructor, type
0529: .getMethods());
0530: }
0531:
0532: /**
0533: * Finds a method in a type.
0534: * Searches for a method with the same name and the same parameter count.
0535: * Parameter types are <b>not</b> compared.
0536: * @param method
0537: * @param type
0538: * @return The first found method or null, if nothing found
0539: * @throws JavaModelException
0540: */
0541: public static IMethod findMethod(IMethod method, IType type)
0542: throws JavaModelException {
0543: return findMethod(method.getElementName(), method
0544: .getParameterTypes().length, method.isConstructor(),
0545: type.getMethods());
0546: }
0547:
0548: /**
0549: * Finds a method in an array of methods.
0550: * Searches for a method with the same name and the same parameter count.
0551: * Parameter types are <b>not</b> compared.
0552: * @param method
0553: * @param methods
0554: * @return The first found method or null, if nothing found
0555: * @throws JavaModelException
0556: */
0557: public static IMethod findMethod(IMethod method, IMethod[] methods)
0558: throws JavaModelException {
0559: return findMethod(method.getElementName(), method
0560: .getParameterTypes().length, method.isConstructor(),
0561: methods);
0562: }
0563:
0564: public static IMethod findMethod(String name, int parameters,
0565: boolean isConstructor, IMethod[] methods)
0566: throws JavaModelException {
0567: for (int i = methods.length - 1; i >= 0; i--) {
0568: IMethod curr = methods[i];
0569: if (name.equals(curr.getElementName())) {
0570: if (isConstructor == curr.isConstructor()) {
0571: if (parameters == curr.getParameterTypes().length) {
0572: return curr;
0573: }
0574: }
0575: }
0576: }
0577: return null;
0578: }
0579:
0580: /**
0581: * Finds a method in a type.
0582: * This searches for a method with the same name and signature. Parameter types are only
0583: * compared by the simple name, no resolving for the fully qualified type name is done
0584: * @param method
0585: * @param type
0586: * @return The first found method or null, if nothing found
0587: * @throws JavaModelException
0588: */
0589: public static IMethod findSimilarMethod(IMethod method, IType type)
0590: throws JavaModelException {
0591: return findSimilarMethod(method, type.getMethods());
0592: }
0593:
0594: /**
0595: * Finds a method in an array of methods.
0596: * This searches for a method with the same name and signature. Parameter types are only
0597: * compared by the simple name, no resolving for the fully qualified type name is done
0598: * @param method
0599: * @param methods
0600: * @return The first found method or null, if nothing found
0601: * @throws JavaModelException
0602: */
0603: public static IMethod findSimilarMethod(IMethod method,
0604: IMethod[] methods) throws JavaModelException {
0605: boolean isConstructor = method.isConstructor();
0606: for (int i = 0; i < methods.length; i++) {
0607: IMethod otherMethod = methods[i];
0608: if (otherMethod.isConstructor() == isConstructor
0609: && method.isSimilar(otherMethod))
0610: return otherMethod;
0611: }
0612: return null;
0613: }
0614:
0615: /*
0616: * Compare two parameter signatures
0617: */
0618: public static boolean compareParamTypes(String[] paramTypes1,
0619: String[] paramTypes2) {
0620: if (paramTypes1.length == paramTypes2.length) {
0621: int i = 0;
0622: while (i < paramTypes1.length) {
0623: String t1 = Signature.getSimpleName(Signature
0624: .toString(paramTypes1[i]));
0625: String t2 = Signature.getSimpleName(Signature
0626: .toString(paramTypes2[i]));
0627: if (!t1.equals(t2)) {
0628: return false;
0629: }
0630: i++;
0631: }
0632: return true;
0633: }
0634: return false;
0635: }
0636:
0637: //---------------------
0638:
0639: public static RefactoringStatus checkIfCuBroken(IMember member)
0640: throws JavaModelException {
0641: ICompilationUnit cu = (ICompilationUnit) JavaCore.create(member
0642: .getCompilationUnit().getResource());
0643: if (cu == null)
0644: return RefactoringStatus
0645: .createFatalErrorStatus(RefactoringCoreMessages.Checks_cu_not_created);
0646: else if (!cu.isStructureKnown())
0647: return RefactoringStatus
0648: .createFatalErrorStatus(RefactoringCoreMessages.Checks_cu_not_parsed);
0649: return new RefactoringStatus();
0650: }
0651:
0652: /**
0653: * From SearchResultGroup[] passed as the parameter
0654: * this method removes all those that correspond to a non-parsable ICompilationUnit
0655: * and returns it as a result.
0656: * @param grouped the array of search result groups from which non parsable compilation
0657: * units are to be removed.
0658: * @param status a refactoring status to collect errors and problems
0659: * @return the array of search result groups
0660: * @throws JavaModelException
0661: */
0662: public static SearchResultGroup[] excludeCompilationUnits(
0663: SearchResultGroup[] grouped, RefactoringStatus status)
0664: throws JavaModelException {
0665: List result = new ArrayList();
0666: boolean wasEmpty = grouped.length == 0;
0667: for (int i = 0; i < grouped.length; i++) {
0668: IResource resource = grouped[i].getResource();
0669: IJavaElement element = JavaCore.create(resource);
0670: if (!(element instanceof ICompilationUnit))
0671: continue;
0672: //XXX this is a workaround for a jcore feature that shows errors in cus only when you get the original element
0673: ICompilationUnit cu = (ICompilationUnit) JavaCore
0674: .create(resource);
0675: if (!cu.isStructureKnown()) {
0676: String path = Checks.getFullPath(cu);
0677: status
0678: .addError(Messages
0679: .format(
0680: RefactoringCoreMessages.Checks_cannot_be_parsed,
0681: path));
0682: continue; //removed, go to the next one
0683: }
0684: result.add(grouped[i]);
0685: }
0686:
0687: if ((!wasEmpty) && result.isEmpty())
0688: status
0689: .addFatalError(RefactoringCoreMessages.Checks_all_excluded);
0690:
0691: return (SearchResultGroup[]) result
0692: .toArray(new SearchResultGroup[result.size()]);
0693: }
0694:
0695: private static final String getFullPath(ICompilationUnit cu) {
0696: Assert.isTrue(cu.exists());
0697: return cu.getResource().getFullPath().toString();
0698: }
0699:
0700: public static RefactoringStatus checkCompileErrorsInAffectedFiles(
0701: SearchResultGroup[] grouped) throws JavaModelException {
0702: RefactoringStatus result = new RefactoringStatus();
0703: for (int i = 0; i < grouped.length; i++)
0704: checkCompileErrorsInAffectedFile(result, grouped[i]
0705: .getResource());
0706: return result;
0707: }
0708:
0709: public static void checkCompileErrorsInAffectedFile(
0710: RefactoringStatus result, IResource resource)
0711: throws JavaModelException {
0712: if (hasCompileErrors(resource))
0713: result
0714: .addWarning(Messages
0715: .format(
0716: RefactoringCoreMessages.Checks_cu_has_compile_errors,
0717: resource.getFullPath()
0718: .makeRelative()));
0719: }
0720:
0721: public static RefactoringStatus checkCompileErrorsInAffectedFiles(
0722: SearchResultGroup[] references, IResource declaring)
0723: throws JavaModelException {
0724: RefactoringStatus result = new RefactoringStatus();
0725: for (int i = 0; i < references.length; i++) {
0726: IResource resource = references[i].getResource();
0727: if (resource.equals(declaring))
0728: declaring = null;
0729: checkCompileErrorsInAffectedFile(result, resource);
0730: }
0731: if (declaring != null)
0732: checkCompileErrorsInAffectedFile(result, declaring);
0733: return result;
0734: }
0735:
0736: private static boolean hasCompileErrors(IResource resource)
0737: throws JavaModelException {
0738: try {
0739: IMarker[] problemMarkers = resource.findMarkers(
0740: IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, true,
0741: IResource.DEPTH_INFINITE);
0742: for (int i = 0; i < problemMarkers.length; i++) {
0743: if (problemMarkers[i]
0744: .getAttribute(IMarker.SEVERITY, -1) == IMarker.SEVERITY_ERROR)
0745: return true;
0746: }
0747: return false;
0748: } catch (JavaModelException e) {
0749: throw e;
0750: } catch (CoreException e) {
0751: throw new JavaModelException(e);
0752: }
0753: }
0754:
0755: //------
0756: public static boolean isReadOnly(Object element)
0757: throws JavaModelException {
0758: if (element instanceof IResource)
0759: return isReadOnly((IResource) element);
0760:
0761: if (element instanceof IJavaElement) {
0762: if ((element instanceof IPackageFragmentRoot)
0763: && isClasspathDelete((IPackageFragmentRoot) element))
0764: return false;
0765: return isReadOnly(((IJavaElement) element).getResource());
0766: }
0767:
0768: Assert.isTrue(false, "not expected to get here"); //$NON-NLS-1$
0769: return false;
0770: }
0771:
0772: public static boolean isReadOnly(IResource res)
0773: throws JavaModelException {
0774: ResourceAttributes attributes = res.getResourceAttributes();
0775: if (attributes != null && attributes.isReadOnly())
0776: return true;
0777:
0778: if (!(res instanceof IContainer))
0779: return false;
0780:
0781: IContainer container = (IContainer) res;
0782: try {
0783: IResource[] children = container.members();
0784: for (int i = 0; i < children.length; i++) {
0785: if (isReadOnly(children[i]))
0786: return true;
0787: }
0788: return false;
0789: } catch (JavaModelException e) {
0790: throw e;
0791: } catch (CoreException e) {
0792: throw new JavaModelException(e);
0793: }
0794: }
0795:
0796: public static boolean isClasspathDelete(IPackageFragmentRoot pkgRoot) {
0797: IResource res = pkgRoot.getResource();
0798: if (res == null)
0799: return true;
0800: IProject definingProject = res.getProject();
0801: if (res.getParent() != null && pkgRoot.isArchive()
0802: && !res.getParent().equals(definingProject))
0803: return true;
0804:
0805: IProject occurringProject = pkgRoot.getJavaProject()
0806: .getProject();
0807: return !definingProject.equals(occurringProject);
0808: }
0809:
0810: //-------- validateEdit checks ----
0811:
0812: public static RefactoringStatus validateModifiesFiles(
0813: IFile[] filesToModify, Object context) {
0814: RefactoringStatus result = new RefactoringStatus();
0815: IStatus status = Resources.checkInSync(filesToModify);
0816: if (!status.isOK())
0817: result.merge(RefactoringStatus.create(status));
0818: status = Resources.makeCommittable(filesToModify, context);
0819: if (!status.isOK()) {
0820: result.merge(RefactoringStatus.create(status));
0821: if (!result.hasFatalError()) {
0822: result
0823: .addFatalError(RefactoringCoreMessages.Checks_validateEdit);
0824: }
0825: }
0826: return result;
0827: }
0828:
0829: public static RefactoringStatus validateEdit(ICompilationUnit unit,
0830: Object context) {
0831: IResource resource = unit.getPrimary().getResource();
0832: RefactoringStatus result = new RefactoringStatus();
0833: if (resource == null)
0834: return result;
0835: IStatus status = Resources.checkInSync(resource);
0836: if (!status.isOK())
0837: result.merge(RefactoringStatus.create(status));
0838: status = Resources.makeCommittable(resource, context);
0839: if (!status.isOK()) {
0840: result.merge(RefactoringStatus.create(status));
0841: if (!result.hasFatalError()) {
0842: result
0843: .addFatalError(RefactoringCoreMessages.Checks_validateEdit);
0844: }
0845: }
0846: return result;
0847: }
0848:
0849: /**
0850: * Checks whether it is possible to modify the given <code>IJavaElement</code>.
0851: * The <code>IJavaElement</code> must exist and be non read-only to be modifiable.
0852: * Moreover, if it is a <code>IMember</code> it must not be binary.
0853: * The returned <code>RefactoringStatus</code> has <code>ERROR</code> severity if
0854: * it is not possible to modify the element.
0855: * @param javaElement
0856: * @return the status
0857: * @throws JavaModelException
0858: *
0859: * @see IJavaElement#exists
0860: * @see IJavaElement#isReadOnly
0861: * @see IMember#isBinary
0862: * @see RefactoringStatus
0863: */
0864: public static RefactoringStatus checkAvailability(
0865: IJavaElement javaElement) throws JavaModelException {
0866: RefactoringStatus result = new RefactoringStatus();
0867: if (!javaElement.exists())
0868: result.addFatalError(Messages.format(
0869: RefactoringCoreMessages.Refactoring_not_in_model,
0870: javaElement.getElementName()));
0871: if (javaElement.isReadOnly())
0872: result.addFatalError(Messages.format(
0873: RefactoringCoreMessages.Refactoring_read_only,
0874: javaElement.getElementName()));
0875: if (javaElement.exists() && !javaElement.isStructureKnown())
0876: result
0877: .addFatalError(Messages
0878: .format(
0879: RefactoringCoreMessages.Refactoring_unknown_structure,
0880: javaElement.getElementName()));
0881: if (javaElement instanceof IMember
0882: && ((IMember) javaElement).isBinary())
0883: result.addFatalError(Messages.format(
0884: RefactoringCoreMessages.Refactoring_binary,
0885: javaElement.getElementName()));
0886: return result;
0887: }
0888:
0889: public static boolean isAvailable(IJavaElement javaElement)
0890: throws JavaModelException {
0891: if (javaElement == null)
0892: return false;
0893: if (!javaElement.exists())
0894: return false;
0895: if (javaElement.isReadOnly())
0896: return false;
0897: // work around for https://bugs.eclipse.org/bugs/show_bug.cgi?id=48422
0898: // the Java project is now cheating regarding its children so we shouldn't
0899: // call isStructureKnown if the project isn't open.
0900: // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=52474
0901: if (!(javaElement instanceof IJavaProject)
0902: && !(javaElement instanceof ILocalVariable)
0903: && !javaElement.isStructureKnown())
0904: return false;
0905: if (javaElement instanceof IMember
0906: && ((IMember) javaElement).isBinary())
0907: return false;
0908: return true;
0909: }
0910:
0911: public static IType findTypeInPackage(IPackageFragment pack,
0912: String name) throws JavaModelException {
0913: Assert.isTrue(pack.exists());
0914: Assert.isTrue(!pack.isReadOnly());
0915:
0916: /* ICompilationUnit.getType expects simple name*/
0917: if (name.indexOf(".") != -1) //$NON-NLS-1$
0918: name = name.substring(0, name.indexOf(".")); //$NON-NLS-1$
0919: ICompilationUnit[] cus = pack.getCompilationUnits();
0920: for (int i = 0; i < cus.length; i++) {
0921: if (cus[i].getType(name).exists())
0922: return cus[i].getType(name);
0923: }
0924: return null;
0925: }
0926:
0927: public static RefactoringStatus checkTempName(String newName,
0928: IJavaElement context) {
0929: RefactoringStatus result = Checks.checkIdentifier(newName,
0930: context);
0931: if (result.hasFatalError())
0932: return result;
0933: if (!Checks.startsWithLowerCase(newName))
0934: result
0935: .addWarning(RefactoringCoreMessages.ExtractTempRefactoring_convention);
0936: return result;
0937: }
0938:
0939: public static RefactoringStatus checkEnumConstantName(
0940: String newName, IJavaElement context) {
0941: RefactoringStatus result = Checks.checkFieldName(newName,
0942: context);
0943: if (result.hasFatalError())
0944: return result;
0945: for (int i = 0; i < newName.length(); i++) {
0946: char c = newName.charAt(i);
0947: if (Character.isLetter(c) && !Character.isUpperCase(c)) {
0948: result
0949: .addWarning(RefactoringCoreMessages.RenameEnumConstRefactoring_convention);
0950: break;
0951: }
0952: }
0953: return result;
0954: }
0955:
0956: public static RefactoringStatus checkConstantName(String newName,
0957: IJavaElement context) {
0958: RefactoringStatus result = Checks.checkFieldName(newName,
0959: context);
0960: if (result.hasFatalError())
0961: return result;
0962: for (int i = 0; i < newName.length(); i++) {
0963: char c = newName.charAt(i);
0964: if (Character.isLetter(c) && !Character.isUpperCase(c)) {
0965: result
0966: .addWarning(RefactoringCoreMessages.ExtractConstantRefactoring_convention);
0967: break;
0968: }
0969: }
0970: return result;
0971: }
0972:
0973: public static boolean isException(IType iType, IProgressMonitor pm)
0974: throws JavaModelException {
0975: try {
0976: if (!iType.isClass())
0977: return false;
0978: IType[] super Types = iType.newSupertypeHierarchy(pm)
0979: .getAllSupertypes(iType);
0980: for (int i = 0; i < super Types.length; i++) {
0981: if ("java.lang.Throwable".equals(super Types[i].getFullyQualifiedName())) //$NON-NLS-1$
0982: return true;
0983: }
0984: return false;
0985: } finally {
0986: pm.done();
0987: }
0988: }
0989:
0990: /**
0991: * @param e
0992: * @return int
0993: * Checks.IS_RVALUE if e is an rvalue
0994: * Checks.NOT_RVALUE_VOID if e is not an rvalue because its type is void
0995: * Checks.NOT_RVALUE_MISC if e is not an rvalue for some other reason
0996: */
0997: public static int checkExpressionIsRValue(Expression e) {
0998: if (e instanceof Name) {
0999: if (!(((Name) e).resolveBinding() instanceof IVariableBinding)) {
1000: return NOT_RVALUE_MISC;
1001: }
1002: }
1003:
1004: ITypeBinding tb = e.resolveTypeBinding();
1005: if (tb == null)
1006: return NOT_RVALUE_MISC;
1007: else if (tb.getName().equals("void")) //$NON-NLS-1$
1008: return NOT_RVALUE_VOID;
1009:
1010: return IS_RVALUE;
1011: }
1012:
1013: public static boolean isDeclaredIn(
1014: VariableDeclaration tempDeclaration, Class astNodeClass) {
1015: ASTNode initializer = ASTNodes.getParent(tempDeclaration,
1016: astNodeClass);
1017: if (initializer == null)
1018: return false;
1019: ASTNode anonymous = ASTNodes.getParent(tempDeclaration,
1020: AnonymousClassDeclaration.class);
1021: if (anonymous == null)
1022: return true;
1023: // stupid code. Is to find out if the variable declaration isn't a field.
1024: if (ASTNodes.isParent(anonymous, initializer))
1025: return false;
1026: return true;
1027: }
1028: }
|