package net.sourceforge.c4jplugin.internal.util; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Vector; import net.sourceforge.c4jplugin.internal.core.ContractReferenceModel; import net.sourceforge.c4jplugin.internal.markers.IClassInvariantMarker; import net.sourceforge.c4jplugin.internal.markers.IContractedClassInvariantMarker; import net.sourceforge.c4jplugin.internal.markers.IContractedMethodMarker; import net.sourceforge.c4jplugin.internal.markers.IMethodMarker; import net.sourceforge.c4jplugin.internal.markers.IProblemMarker; import net.sourceforge.c4jplugin.internal.ui.text.UIMessages; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceVisitor; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaModelMarker; import org.eclipse.jdt.core.IMember; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.osgi.util.NLS; public class ContractReferenceUtil { private static boolean DEBUG = false; static public void deleteMarkers(IProject project) throws CoreException { project.deleteMarkers(IClassInvariantMarker.ID, true, IResource.DEPTH_INFINITE); project.deleteMarkers(IContractedClassInvariantMarker.ID, true, IResource.DEPTH_INFINITE); project.deleteMarkers(IContractedMethodMarker.ID, true, IResource.DEPTH_INFINITE); project.deleteMarkers(IMethodMarker.ID, true, IResource.DEPTH_INFINITE); project.deleteMarkers(IProblemMarker.ID, true, IResource.DEPTH_INFINITE); } static public void deleteMarkers(IResource resource) throws CoreException{ resource.deleteMarkers(IClassInvariantMarker.ID, true, IResource.DEPTH_ZERO); resource.deleteMarkers(IContractedClassInvariantMarker.ID, true, IResource.DEPTH_ZERO); resource.deleteMarkers(IMethodMarker.ID, true, IResource.DEPTH_ZERO); resource.deleteMarkers(IContractedMethodMarker.ID, true, IResource.DEPTH_ZERO); resource.deleteMarkers(IProblemMarker.ID, true, IResource.DEPTH_ZERO); } static public void createContractMarkers(IResource contract) { if (!ContractReferenceModel.isContract(contract)) return; //System.out.println("creating contract markers"); IJavaElement jelement = JavaCore.create(contract); try { IType type = getType(jelement); if (type != null) { ASTParser parser = ASTParser.newParser(AST.JLS3); parser.setSource(type.getCompilationUnit()); CompilationUnit cu = (CompilationUnit)parser.createAST(null); // cache all IContractedMethodMarker makers of the target Vector<IMarker> methodMarkers = new Vector<IMarker>(); for (IResource contractedRes : ContractReferenceModel.getContractedClasses(contract)) { IMarker[] markers = contractedRes.findMarkers(IContractedMethodMarker.ID, true, IResource.DEPTH_ZERO); methodMarkers.addAll(Arrays.asList(markers)); } // check every method in the contract if it really enforces a contract // on another method in some class or if it is a classInvariant method for (IMethod method : type.getMethods()) { String name = method.getElementName(); String contractType = null; String methodName = null; if (name.startsWith("pre_")) { //$NON-NLS-1$ contractType = IMethodMarker.VALUE_PRE_METHOD; methodName = name.substring("pre_".length()); //$NON-NLS-1$ } else if (name.startsWith("post_")) { //$NON-NLS-1$ contractType = IMethodMarker.VALUE_POST_METHOD; methodName = name.substring("post_".length()); //$NON-NLS-1$ } else if (name.equals("classInvariant") && method.getSignature().equals("()V")) { //$NON-NLS-1$ //$NON-NLS-2$ IMarker methodMarker = contract.createMarker(IClassInvariantMarker.ID); methodMarker.setAttribute(IMarker.LINE_NUMBER, cu.getLineNumber(method.getNameRange().getOffset())); methodMarker.setAttribute(IMethodMarker.ATTR_CONTRACT_TYPE, IMethodMarker.VALUE_CLASS_INVARIANT); methodMarker.setAttribute(IMethodMarker.ATTR_HANDLE_IDENTIFIER, method.getHandleIdentifier()); methodMarker.setAttribute(IMarker.MESSAGE, UIMessages.MarkerMessage_contract_classInvariant); } if (contractType != null) { // the contract method is a "pre_" or "post_" method // check for methods which are guarded by it boolean isContracting = false; String methodHandle = null; // checks all methods in sources which are located inside // the current workspace for (IMarker marker : methodMarkers) { String markerType = marker.getAttribute(IContractedMethodMarker.ATTR_CONTRACT_TYPE, ""); //$NON-NLS-1$ if (markerType.equals(contractType) || markerType.equals(IContractedMethodMarker.VALUE_PREPOST_METHOD)) { methodHandle = marker.getAttribute(IContractedMethodMarker.ATTR_HANDLE_IDENTIFIER, null); // check if the found method has the same paramter types // and the same name without "pre_" or "post_" IMethod contractedMethod = (IMethod)JavaCore.create(methodHandle); if (Arrays.equals(contractedMethod.getParameterTypes(), method.getParameterTypes()) && contractedMethod.getElementName().equals(methodName)) { // yes, create a marker for the contract method isContracting = true; break; } } } // try the supertype hierarchy of the contracts target boolean bSuperMethod = false; if (!isContracting) { IResource target = ContractReferenceModel.getTarget(contract); if (target != null) { IType typeTarget = getType(JavaCore.create(target)); if (typeTarget != null) { IType[] superTypes = typeTarget.newSupertypeHierarchy(null).getAllSupertypes(typeTarget); for (IType superType : superTypes) { for (IMethod superMethod : superType.getMethods()) { if (Arrays.equals(superMethod.getParameterTypes(), method.getParameterTypes()) && superMethod.getElementName().equals(methodName)) { isContracting = true; bSuperMethod = true; break; } } if (isContracting) break; } } } } if (isContracting) { IMarker methodMarker = contract.createMarker(IMethodMarker.ID); methodMarker.setAttribute(IMethodMarker.ATTR_CONTRACT_TYPE, contractType); methodMarker.setAttribute(IMarker.LINE_NUMBER, cu.getLineNumber(method.getNameRange().getOffset())); methodMarker.setAttribute(IMethodMarker.ATTR_HANDLE_IDENTIFIER, method.getHandleIdentifier()); if (bSuperMethod) methodMarker.setAttribute(IMarker.MESSAGE, UIMessages.MarkerMessage_contract_methodIsContracting_super); else methodMarker.setAttribute(IMarker.MESSAGE, UIMessages.MarkerMessage_contract_methodIsContracting); } else { // the method contract is not enforced on any method IMarker methodMarker = contract.createMarker(IProblemMarker.ID); methodMarker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_WARNING); methodMarker.setAttribute(IMarker.CHAR_START, method.getNameRange().getOffset()); methodMarker.setAttribute(IMarker.CHAR_END, method.getNameRange().getOffset()+method.getNameRange().getLength()); methodMarker.setAttribute(IMarker.LINE_NUMBER, cu.getLineNumber(method.getNameRange().getOffset())); methodMarker.setAttribute(IMarker.MESSAGE, UIMessages.MarkerMessage_problem_methodNotContracting); } } } } } catch (JavaModelException e) {} catch (CoreException e) {} } static public void createContractedClassMarkers(IResource resource) { Collection<IResource> references = ContractReferenceModel.getContracts(resource); if (references == null || references.size() == 0) return; IJavaElement jelement = JavaCore.create(resource); IType type = null; try { type = getType(jelement); if (type != null) { ASTParser parser = ASTParser.newParser(AST.JLS3); parser.setSource(type.getCompilationUnit()); CompilationUnit cu = (CompilationUnit)parser.createAST(null); IMarker markerClassInvariant = null; int invariantCounter = 0; Vector<IType> typeCache = new Vector<IType>(); // checking for class invariants String invariantList = ""; //$NON-NLS-1$ for (IResource reference : references) { IJavaElement refElement = JavaCore.create(reference); IType refType = getType(refElement); typeCache.add(refType); IMethod methodClassInvariant = refType.getMethod("classInvariant", new String[] {}); //$NON-NLS-1$ if (methodClassInvariant != null && methodClassInvariant.exists()) { invariantCounter++; invariantList += " " + reference.getName(); //$NON-NLS-1$ try { if (markerClassInvariant == null) { markerClassInvariant = resource.createMarker(IContractedClassInvariantMarker.ID); markerClassInvariant.setAttribute(IMarker.LINE_NUMBER, cu.getLineNumber(refType.getNameRange().getOffset())); } } catch (CoreException e) {} } } if (markerClassInvariant != null) { try { if (invariantList.length() > 0) invariantList = "\n\n" + invariantList; //$NON-NLS-1$ markerClassInvariant.setAttribute(IMarker.MESSAGE, NLS.bind(UIMessages.MarkerMessage_contracted_classInvariant, invariantCounter) + invariantList); } catch (CoreException e) {} } // checking for pre and post methods for (IMethod method : type.getMethods()) { boolean postMethod = false; boolean preMethod = false; String contractList = ""; //$NON-NLS-1$ for (IType refType : typeCache) { boolean refAdded = false; if (refType.getMethod("post_" + method.getElementName(), method.getParameterTypes()).exists()) { //$NON-NLS-1$ postMethod = true; contractList += " " + refType.getCompilationUnit().getCorrespondingResource().getName(); //$NON-NLS-1$ refAdded = true; } if (refType.getMethod("pre_" + method.getElementName(), method.getParameterTypes()).exists()) { //$NON-NLS-1$ preMethod = true; if (!refAdded) { contractList += " " + refType.getCompilationUnit().getCorrespondingResource().getName(); //$NON-NLS-1$ } } } try { IMarker marker = null; if (postMethod && preMethod) { marker = resource.createMarker(IContractedMethodMarker.ID); marker.setAttribute(IContractedMethodMarker.ATTR_CONTRACT_TYPE, IContractedMethodMarker.VALUE_PREPOST_METHOD); marker.setAttribute(IMarker.MESSAGE, UIMessages.MarkerMessage_contracted_prepostMethod + contractList); } else if (postMethod) { marker = resource.createMarker(IContractedMethodMarker.ID); marker.setAttribute(IContractedMethodMarker.ATTR_CONTRACT_TYPE, IContractedMethodMarker.VALUE_POST_METHOD); marker.setAttribute(IMarker.MESSAGE, UIMessages.MarkerMessage_contracted_postMethod + contractList); } else if (preMethod) { marker = resource.createMarker(IContractedMethodMarker.ID); marker.setAttribute(IContractedMethodMarker.ATTR_CONTRACT_TYPE, IContractedMethodMarker.VALUE_PRE_METHOD); marker.setAttribute(IMarker.MESSAGE, UIMessages.MarkerMessage_contracted_preMethod + contractList); } if (marker != null) { marker.setAttribute(IMarker.LINE_NUMBER, cu.getLineNumber(method.getNameRange().getOffset())); marker.setAttribute(IContractedMethodMarker.ATTR_HANDLE_IDENTIFIER, method.getHandleIdentifier()); } } catch (CoreException e) {} } } } catch (JavaModelException e) {} } /** * Checks the given resource for contracts. Does nothing if the resource * is already known to the underlying model (remove it first if you want * to force a recheck). * * @param resource * @return All contracts for the given resource */ static public Collection<IResource> checkResourceForContracts(IResource resource) { try { if (DEBUG) System.out.println("Checking for contracts: " + resource.getName()); Boolean contracted = ContractReferenceModel.isContracted(resource); if (contracted != null && contracted == true) return ContractReferenceModel.getContracts(resource); else if (contracted != null && contracted == false) return Collections.emptyList(); if (DEBUG) System.out.println("Creating Java Element from resource"); IJavaElement javaElement = JavaCore.create(resource); IType type = getType(javaElement); if (type != null) { if (DEBUG) System.out.println("Trying to get direct contract reference"); IResource ref = AnnotationUtil.getContractReference(resource); if (ref != null) { // we found a class which is directly contracted if (DEBUG) System.out.println("Found direct contract " + ref.getName()); ContractReferenceModel.addDirectContract(resource, ref); } if (DEBUG) System.out.println("Searching for super contracts"); // search all super types for contracts Vector<IResource> contractReferences = new Vector<IResource>(); IType[] superTypes = type.newSupertypeHierarchy(null).getSupertypes(type); for (IType superType : superTypes) { IResource superResource = superType.getResource(); if (superResource == null) continue; if (DEBUG) System.out.println("Checking supertype " + superResource.getName() + " for contracts"); contracted = ContractReferenceModel.isContracted(superResource); if (contracted == null) { // the supertype has not yet been checked for contracts contractReferences.addAll(checkResourceForContracts(superResource)); } else if (contracted == true) { contractReferences.addAll(ContractReferenceModel.getContracts(superResource)); } } if (contractReferences.size() > 0) { // this type has supercontracts if (DEBUG) System.out.println("Resource " + resource.getName() + " has " + contractReferences.size() + " contracts"); ContractReferenceModel.addSuperContracts(resource, contractReferences); } if (ref != null) contractReferences.add(ref); return contractReferences; } } catch (JavaModelException e) { e.printStackTrace(); } return Collections.emptyList(); } /** * Checks all subtypes of <em>resource</em> for contracts. * * @param resource * @return All subtypes for which contracts have changed */ static public Collection<IResource> checkAllSubtypes(IResource resource) { Vector<IResource> resources = new Vector<IResource>(); try { IType type = getType(JavaCore.create(resource)); if (type == null) return Collections.emptyList(); IType[] subTypes = type.newTypeHierarchy(type.getJavaProject(), null).getAllSubtypes(type); for (IType subType : subTypes) { IResource subResource = subType.getCompilationUnit().getCorrespondingResource(); Boolean wasContracted = ContractReferenceModel.isContracted(subResource); deleteMarkers(subResource); ContractReferenceModel.removeContractedClass(subResource); //ContractReferenceModel.clearSessionProperties(subResource); boolean isContracted = !checkResourceForContracts(subResource).isEmpty(); if ((wasContracted != null && wasContracted == true) != isContracted) { resources.add(subType.getResource()); } } } catch (JavaModelException e) {} catch (CoreException e) {} return resources; } static public void refreshModel(IProject project, IProgressMonitor monitor, boolean clear) { try { monitor.beginTask(UIMessages.Builder_startRefreshContractModel, 102); if (clear) ContractReferenceModel.clearModel(project); monitor.worked(1); if (monitor.isCanceled()) throw new OperationCanceledException(); int sourceCount = getNumberOfSourceFiles(project); monitor.worked(2); if (monitor.isCanceled()) throw new OperationCanceledException(); IProgressMonitor submonitor = new SubProgressMonitor(monitor, 50); ContractResourceVisitor contractVisitor = new ContractResourceVisitor(submonitor); try { submonitor.beginTask(UIMessages.Builder_checkingContractedClasses, sourceCount); project.accept(contractVisitor); } catch (CoreException e) { // TODO error handling in case something went wrong refreshing the model e.printStackTrace(); } finally { submonitor.done(); } if (submonitor.isCanceled()) throw new OperationCanceledException(); submonitor = new SubProgressMonitor(monitor, 50); Collection<IResource> contracts = contractVisitor.getContracts(); if (DEBUG) System.out.println("found " + contracts.size() + " contracts:"); try { submonitor.beginTask(UIMessages.Builder_creatingContractMarkers, contracts.size()); for (IResource contract : contracts) { if (DEBUG) System.out.println(contract.getName()); if (submonitor.isCanceled()) throw new OperationCanceledException(); createContractMarkers(contract); submonitor.worked(1); } } finally { submonitor.done(); } } finally { monitor.done(); } } static public IType getType(IJavaElement element) throws JavaModelException { IType type = null; if (element instanceof ICompilationUnit) { type = ((ICompilationUnit) element).findPrimaryType(); } else if (element instanceof IClassFile) { type = ((IClassFile)element).getType(); } else if (element instanceof IType) { type = (IType) element; } else if (element instanceof IMember) { type = ((IMember)element).getDeclaringType(); } return type; } static public boolean hasJavaErrors(IType type) { try { IMarker[] markers = type.getCompilationUnit().getCorrespondingResource().findMarkers( IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE); for (IMarker marker : markers) { if (marker.getAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO) == IMarker.SEVERITY_ERROR) return true; } return false; } catch (JavaModelException e) { e.printStackTrace(); } catch (CoreException e) { e.printStackTrace(); } return false; } static private int getNumberOfSourceFiles(IProject project) { CountingResourceVisitor countingResourceVisitor = new CountingResourceVisitor(); try { project.accept(countingResourceVisitor); } catch (CoreException e) {} return countingResourceVisitor.counter; } private static class CountingResourceVisitor implements IResourceVisitor { int counter = 0; public boolean visit(IResource resource) throws CoreException { if (resource.getName().endsWith(".java")) { //$NON-NLS-1$ counter++; return false; } return true; } } private static class ContractResourceVisitor implements IResourceVisitor { private IProgressMonitor monitor; private Collection<IResource> contracts = new HashSet<IResource>(); public ContractResourceVisitor(IProgressMonitor monitor) { this.monitor = monitor; } public Collection<IResource> getContracts() { return contracts; } public boolean visit(IResource resource) throws CoreException { if (monitor.isCanceled()) throw new OperationCanceledException(); //System.out.println("Checking resource: " + resource.getName()); if (resource.getName().endsWith(".java")) { //$NON-NLS-1$ contracts.addAll(checkResourceForContracts(resource)); createContractedClassMarkers(resource); monitor.worked(1); return false; } return true; } } }