package net.sourceforge.c4jplugin.internal.util; import java.util.Arrays; import java.util.Collection; import java.util.Collections; 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.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 { 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; 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 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_")) { contractType = IMethodMarker.VALUE_PRE_METHOD; methodName = name.substring("pre_".length()); } else if (name.startsWith("post_")) { contractType = IMethodMarker.VALUE_POST_METHOD; methodName = name.substring("post_".length()); } else if (name.equals("classInvariant") && method.getSignature().equals("()V")) { 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; for (IMarker marker : methodMarkers) { String markerType = marker.getAttribute(IContractedMethodMarker.ATTR_CONTRACT_TYPE, ""); 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 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()); methodMarker.setAttribute(IMarker.MESSAGE, UIMessages.MarkerMessage_contract_methodIsContracting); isContracting = true; break; } } } if (!isContracting) { // 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.getContractReferences(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 = ""; for (IResource reference : references) { IJavaElement refElement = JavaCore.create(reference); IType refType = getType(refElement); typeCache.add(refType); IMethod methodClassInvariant = refType.getMethod("classInvariant", new String[] {}); if (methodClassInvariant != null && methodClassInvariant.exists()) { invariantCounter++; invariantList += " - " + reference.getName() + "\n"; 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; 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 = ""; for (IType refType : typeCache) { boolean refAdded = false; if (refType.getMethod("post_" + method.getElementName(), method.getParameterTypes()).exists()) { postMethod = true; contractList += " - " + refType.getCompilationUnit().getCorrespondingResource().getName() + "\n"; refAdded = true; } if (refType.getMethod("pre_" + method.getElementName(), method.getParameterTypes()).exists()) { preMethod = true; if (!refAdded) { contractList += " - " + refType.getCompilationUnit().getCorrespondingResource().getName() + "\n"; } } } if (contractList.length() > 0) contractList = "\n\n" + contractList; 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) {} } static public Collection<IResource> checkResourceForContracts(IResource resource) { try { Boolean contracted = ContractReferenceModel.isContracted(resource); if (contracted != null && contracted == true) return ContractReferenceModel.getContractReferences(resource); else if (contracted != null && contracted == false) return Collections.emptyList(); IJavaElement javaElement = JavaCore.create(resource); IType type = getType(javaElement); if (type != null) { Vector<IResource> contractReferences = new Vector<IResource>(); IResource ref = AnnotationUtil.getContractReference(resource); if (ref != null) { // we found a class which is directly contracted contractReferences.add(ref); } // search all super types for contracts IType[] superTypes = type.newSupertypeHierarchy(null).getSupertypes(type); for (IType superType : superTypes) { IResource superResource = superType.getResource(); if (superResource == null) continue; 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.getContractReferences(superResource)); } } if (contractReferences.size() > 0) { // this type is contracted ContractReferenceModel.setContractReferences(resource, contractReferences); return contractReferences; } else { ContractReferenceModel.setContractReferences(resource, null); } } } catch (JavaModelException e) {} return Collections.emptyList(); } static public Collection<IResource> checkAllSubtypes(IResource resource, boolean clean) { 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); if (clean) { deleteMarkers(subResource); ContractReferenceModel.clearResource(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) { monitor.beginTask(UIMessages.Builder_startRefreshContractModel, 102); if (clear) ContractReferenceModel.clearModel(project); monitor.worked(1); int sourceCount = getNumberOfSourceFiles(project); monitor.worked(2); IProgressMonitor submonitor = new SubProgressMonitor(monitor, 50); submonitor.beginTask(UIMessages.Builder_checkingContractedClasses, sourceCount); IResourceVisitor contractVisitor = new ContractResourceVisitor(submonitor); try { project.accept(contractVisitor); } catch (CoreException e) { // TODO error handling in case something went wrong refreshing the model e.printStackTrace(); } submonitor.done(); submonitor = new SubProgressMonitor(monitor, 50); Collection<IResource> contracts = ContractReferenceModel.getAllContracts(); submonitor.beginTask(UIMessages.Builder_creatingContractMarkers, contracts.size()); for (IResource contract : contracts) { createContractMarkers(contract); submonitor.worked(1); } submonitor.done(); 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); if (markers.length > 0) 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")) { counter++; return false; } return true; } } private static class ContractResourceVisitor implements IResourceVisitor { private IProgressMonitor monitor; public ContractResourceVisitor(IProgressMonitor monitor) { this.monitor = monitor; } public boolean visit(IResource resource) throws CoreException { if (resource.getName().endsWith(".java")) { checkResourceForContracts(resource); createContractedClassMarkers(resource); monitor.worked(1); return false; } return true; } } }