/* * Copyright (c) 2012 Andrejs Jermakovics. * * 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: * Andrejs Jermakovics - initial implementation */ package jmockit.assist; import static org.eclipse.core.resources.IMarker.SEVERITY_ERROR; import static org.eclipse.core.resources.IMarker.SEVERITY_WARNING; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import jmockit.assist.prefs.Prefs; import jmockit.assist.prefs.Prefs.CheckScope; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.WorkspaceJob; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.compiler.BuildContext; import org.eclipse.jdt.core.compiler.CategorizedProblem; import org.eclipse.jdt.core.compiler.CompilationParticipant; import org.eclipse.jdt.core.compiler.ReconcileContext; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.ui.progress.IProgressConstants; /** * Compilation participant to check mock classes and methods for problems */ public final class JMockitCompilationParticipant extends CompilationParticipant { private static final int JOB_DELAY = 2000; //private static final int JOB_DELAY = 2000; public static final String MARKER = "jmockit.eclipse.marker"; private AnalysisJob job = new AnalysisJob(); @Override public void buildFinished(final IJavaProject project) { } @Override public void buildStarting(final BuildContext[] files, final boolean isBatch) { CheckScope scope = getCheckScope(); IResource res = Activator.getActiveResource(); String activeProj = null; if( res != null ) { activeProj = res.getProject().getName(); } List<BuildContext> filesToParse = new ArrayList<BuildContext>(); for (BuildContext f : files) { IFile file = f.getFile(); if( (scope == CheckScope.Project || scope == CheckScope.File ) && !file.getProject().getName().equals(activeProj)) { continue; } if( scope == CheckScope.File && (res == null || !file.equals(res)) ) { continue; } filesToParse.add(f); } job.addFiles(filesToParse); if( isBatch ) { job.schedule(JOB_DELAY); } else { job.schedule(); } } public static CheckScope getCheckScope() { String propVal = Activator.getPrefStore().getString(Prefs.PROP_CHECK_SCOPE); CheckScope scope = CheckScope.Disabled; try { scope = CheckScope.valueOf(propVal); } catch(Exception e) { Activator.log(e); } return scope; } @Override public boolean isActive(final IJavaProject project) { CheckScope checkScope = getCheckScope(); if( checkScope == CheckScope.Disabled || !project.isOpen() ) { return false; } if( (checkScope == CheckScope.Project || checkScope == CheckScope.File ) && !project.getProject().getName().equals(Activator.getActiveProject()) ) { return false; } IType mockitType = null; try { mockitType = project.findType(MockUtil.MOCK); if (mockitType == null) { mockitType = project.findType(MockUtil.MOCKIT); } } catch (JavaModelException e) { Activator.log(e); } return mockitType != null; } @Override public void reconcile(final ReconcileContext context) { if( getCheckScope() == CheckScope.Disabled ) { return; } try { ICompilationUnit cunit = context.getWorkingCopy(); if( cunit.isStructureKnown() ) { MockASTVisitor visitor = new MockASTVisitor(cunit); context.getAST3().accept(visitor); CategorizedProblem[] probs = visitor.getProblems(); if (probs.length != 0) { context.putProblems(probs[0].getMarkerType(), probs); } } } catch (JavaModelException e) { Activator.log(e); } } private static class AnalysisJob extends WorkspaceJob { private Queue<BuildContext> files = new ConcurrentLinkedQueue<BuildContext>(); public AnalysisJob() { super("JMockit analysis"); setSystem(false); setProperty(IProgressConstants.KEEP_PROPERTY, Boolean.FALSE); } void addFiles( final Collection<BuildContext> buildContexts) { files.addAll(buildContexts); } @Override public IStatus runInWorkspace(final IProgressMonitor mon) throws CoreException { String taskName = "JMockit file analysis"; int workSize = files.size(), worked = 0; mon.beginTask(taskName, workSize); BuildContext f = files.poll(); while( f != null && !mon.isCanceled() ) { IFile file = f.getFile(); if( file.isAccessible() && !file.isDerived(IResource.CHECK_ANCESTORS) ) { ICompilationUnit cunit = JavaCore.createCompilationUnitFrom(file); try { mon.setTaskName(taskName + " - " + cunit.getElementName()); analyseFile(file, cunit, mon); } catch (Exception e) { Activator.log(e); setProperty(IProgressConstants.KEEP_PROPERTY, Boolean.TRUE); files.clear(); return Activator.createStatus(e); } } mon.worked(1); f = files.poll(); worked++; if( files.size() > workSize - worked ) // added more files { workSize = files.size() + worked; mon.beginTask(taskName, workSize); mon.worked(worked); } } if( mon.isCanceled() ) { files.clear(); return Status.CANCEL_STATUS; } mon.done(); return Status.OK_STATUS; } public void analyseFile(final IFile file, final ICompilationUnit cunit, final IProgressMonitor mon) throws JavaModelException, CoreException { if ( cunit != null && cunit.exists() && cunit.isStructureKnown() ) { CompilationUnit cu = ASTUtil.getAstOrParse(cunit, mon); MockASTVisitor visitor = new MockASTVisitor(cunit); cu.accept(visitor); CategorizedProblem[] probs = visitor.getProblems(); file.deleteMarkers(MARKER, true, IResource.DEPTH_INFINITE); for (CategorizedProblem prob : probs) //f.recordNewProblems(probs); { createProblemMarker(file, prob); } } } private static void createProblemMarker(final IFile file, final CategorizedProblem prob) throws CoreException { IMarker marker = file.createMarker(MARKER); marker.setAttribute(IMarker.TRANSIENT, true); marker.setAttribute(IMarker.MESSAGE, prob.getMessage()); marker.setAttribute(IMarker.LINE_NUMBER, prob.getSourceLineNumber()); marker.setAttribute(IMarker.CHAR_START, prob.getSourceStart()); marker.setAttribute(IMarker.CHAR_END, prob.getSourceEnd()); marker.setAttribute(IMarker.SEVERITY, prob.isError()?SEVERITY_ERROR:SEVERITY_WARNING); } } }