1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-01 06:05:24 +02:00

Bug 309607 lots of refactoring and changes to support better marker management - prevent fickering and improve performance

This commit is contained in:
Alena Laskavaia 2010-08-05 02:11:18 +00:00
parent 4fa5682c4d
commit e4c1d89d1a
13 changed files with 534 additions and 79 deletions

View file

@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Bundle-SymbolicName: org.eclipse.cdt.codan.core;singleton:=true
Bundle-Version: 1.1.0.qualifier
Bundle-Version: 2.0.0.qualifier
Bundle-Activator: org.eclipse.cdt.codan.core.CodanCorePlugin
Bundle-Vendor: %Bundle-Vendor
Require-Bundle: org.eclipse.core.runtime,

View file

@ -11,6 +11,7 @@
package org.eclipse.cdt.codan.core.model;
import org.eclipse.cdt.codan.core.CodanRuntime;
import org.eclipse.cdt.codan.internal.core.CheckerInvocationContext;
import org.eclipse.cdt.codan.internal.core.CheckersRegistry;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
@ -22,6 +23,10 @@ import org.eclipse.core.resources.IResource;
*/
public abstract class AbstractChecker implements IChecker {
protected String name;
/**
* @since 2.0
*/
protected ICheckerInvocationContext context;
/**
* Default constructor
@ -34,7 +39,7 @@ public abstract class AbstractChecker implements IChecker {
* false checker's "processResource" method won't be called
*/
public boolean enabledInContext(IResource res) {
return true;
return res instanceof IFile;
}
/**
@ -93,9 +98,14 @@ public abstract class AbstractChecker implements IChecker {
/**
* @return problem reporter for given checker
* @since 2.0
*/
protected IProblemReporter getProblemReporter() {
return CodanRuntime.getInstance().getProblemReporter();
public IProblemReporter getProblemReporter() {
try {
return getContext().getProblemReporter();
} catch (Exception e) {
return CodanRuntime.getInstance().getProblemReporter();
}
}
/**
@ -158,4 +168,63 @@ public abstract class AbstractChecker implements IChecker {
Object... args) {
getProblemReporter().reportProblem(problemId, loc, args);
}
/**
* Get invocation context.
*
* @return checker invocation context
*
* @since 2.0
*/
public ICheckerInvocationContext getContext() {
return context;
}
/**
* Set the invocation context. Usually called by codan builder.
* Object that calls this should also synchronize of checker object
* to prevent multi-thread access to a running context
*
* @since 2.0
*/
public void setContext(ICheckerInvocationContext context) {
this.context = context;
}
/**
* @since 2.0
*/
public boolean before(IResource resource) {
IChecker checker = this;
IProblemReporter problemReporter = CodanRuntime.getInstance()
.getProblemReporter();
IProblemReporter sessionReporter = problemReporter;
if (problemReporter instanceof IProblemReporterSessionPersistent) {
// create session problem reporter
sessionReporter = ((IProblemReporterSessionPersistent) problemReporter)
.createReporter(resource, checker);
((IProblemReporterSessionPersistent) sessionReporter).start();
} else if (problemReporter instanceof IProblemReporterPersistent) {
// delete markers if checker can possibly run on this
// resource this way if checker is not enabled markers would be
// deleted too
((IProblemReporterPersistent) problemReporter).deleteProblems(
resource, checker);
}
((AbstractChecker) checker).setContext(new CheckerInvocationContext(
resource, sessionReporter));
return true;
}
/**
* @since 2.0
*/
public boolean after(IResource resource) {
if (getContext().getProblemReporter() instanceof IProblemReporterSessionPersistent) {
// delete general markers
((IProblemReporterSessionPersistent) getContext()
.getProblemReporter()).done();
}
return true;
}
}

View file

@ -35,7 +35,7 @@ public abstract class AbstractProblemLocation implements IProblemLocation {
}
/**
* @since 1.1
* @since 2.0
*/
protected AbstractProblemLocation(IResource file, int line) {
this.file = file;
@ -49,7 +49,7 @@ public abstract class AbstractProblemLocation implements IProblemLocation {
}
/**
* @since 1.1
* @since 2.0
*/
protected AbstractProblemLocation(IResource file, int startChar, int endChar) {
this.file = file;
@ -87,7 +87,7 @@ public abstract class AbstractProblemLocation implements IProblemLocation {
/**
* @return resource for which marker is created
* @since 1.1
* @since 2.0
*/
public IResource getResource() {
return file;
@ -124,4 +124,42 @@ public abstract class AbstractProblemLocation implements IProblemLocation {
public int getEndingChar() {
return posEnd;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((extra == null) ? 0 : extra.hashCode());
result = prime * result + ((file == null) ? 0 : file.hashCode());
result = prime * result + line;
result = prime * result + posEnd;
result = prime * result + posStart;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof AbstractProblemLocation))
return false;
AbstractProblemLocation other = (AbstractProblemLocation) obj;
if (line != other.line)
return false;
if (posEnd != other.posEnd)
return false;
if (posStart != other.posStart)
return false;
if (extra == null) {
if (other.extra != null)
return false;
} else if (!extra.equals(other.extra))
return false;
if (file == null) {
if (other.file != null)
return false;
} else if (!file.equals(other.file))
return false;
return true;
}
}

View file

@ -17,7 +17,7 @@ import org.eclipse.core.resources.IResource;
/**
* Abstract implementation of a IProblemReporter
*
* @since 1.1
* @since 2.0
*/
public abstract class AbstractProblemReporter implements IProblemReporter {
public void reportProblem(String id, IProblemLocation loc, Object... args) {

View file

@ -60,7 +60,7 @@ public enum CodanSeverity {
/**
* @param intValue
* @return value of severity by its integer value
* @since 1.1
* @since 2.0
*/
public static CodanSeverity valueOf(int intValue) {
if (intValue == IMarker.SEVERITY_INFO)

View file

@ -37,6 +37,21 @@ public interface IChecker {
*/
boolean processResource(IResource resource);
/**
* @since 2.0
*/
boolean before(IResource resource);
/**
* @since 2.0
*/
boolean after(IResource resource);
/**
* @since 2.0
*/
IProblemReporter getProblemReporter();
/**
* Implement this method to trim down type of resource you are interested
* in, usually it will be c/c++ files only. This method should be

View file

@ -0,0 +1,34 @@
/*******************************************************************************
* Copyright (c) 2009,2010 QNX Software Systems
* 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:
* QNX Software Systems (Alena Laskavaia) - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.codan.core.model;
import org.eclipse.core.resources.IResource;
/**
* Since there is only one instance of checker available this object keeps
* track of invocation context - which would usually contain resource and some
* other object that checker require
* <p>
* <strong>EXPERIMENTAL</strong>. This class or interface has been added as part
* of a work in progress. There is no guarantee that this API will work or that
* it will remain the same.
* </p>
*
* @noextend This interface is not intended to be extended by clients.
* @noimplement This interface is not intended to be implemented by clients.
*
* @since 2.0
*/
public interface ICheckerInvocationContext {
IResource getResource();
IProblemReporter getProblemReporter();
}

View file

@ -18,7 +18,7 @@ import org.eclipse.core.runtime.CoreException;
* Instance of a problem. Intermediate representation before problem become a
* marker.
*
* @since 1.1
* @since 2.0
*/
public interface ICodanProblemMarker {
/**
@ -56,4 +56,9 @@ public interface ICodanProblemMarker {
* @return message
*/
public String createMessage();
/**
* @return problem arguments
*/
public Object[] getArgs();
}

View file

@ -0,0 +1,66 @@
/*******************************************************************************
* Copyright (c) 2009, 2010 Alena Laskavaia
* 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:
* Alena Laskavaia - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.codan.core.model;
import org.eclipse.core.resources.IResource;
/**
* IProblemReporterPersistent - interface to report problems, which are
* persistent, ex. markers. Also this object has context of checker and
* current resource, which allows to manage markers better - i.e. instead of
* deleting replace them when needed, and queue markers for insertion instead
* of add right away.
*
* <p>
* <strong>EXPERIMENTAL</strong>. This class or interface has been added as part
* of a work in progress. There is no guarantee that this API will work or that
* it will remain the same.
* </p>
*
* @since 2.0
*/
public interface IProblemReporterSessionPersistent extends IProblemReporter {
/**
* Delete all problems associated with session resource and session checker.
* If "all" is true also delete all problems associated with workspace (and
* session checker)
*
*/
public void deleteProblems(boolean all);
/**
* Notify that session is started
*/
public void start();
/**
* Notify that session is
* ended
*/
public void done();
IChecker getChecker();
IResource getResource();
/**
* Create an instance of the object.This is a bit ugly since implemented has
* to combine
* object itself and factory to this object.
*
* @param resource
* @param checker
* @return
* @since 2.0
*/
public IProblemReporterSessionPersistent createReporter(IResource resource,
IChecker checker);
}

View file

@ -0,0 +1,41 @@
/*******************************************************************************
* Copyright (c) 2009,2010 QNX Software Systems
* 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:
* QNX Software Systems (Alena Laskavaia) - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.codan.internal.core;
import org.eclipse.cdt.codan.core.model.ICheckerInvocationContext;
import org.eclipse.cdt.codan.core.model.IProblemReporter;
import org.eclipse.core.resources.IResource;
/**
* Implementation of ICheckerInvocationContext
*/
public class CheckerInvocationContext implements ICheckerInvocationContext {
private IResource resource;
private IProblemReporter sessionReporter;
/**
* @param resource
* @param sessionReporter
*/
public CheckerInvocationContext(IResource resource,
IProblemReporter sessionReporter) {
this.resource = resource;
this.sessionReporter = sessionReporter;
}
public IResource getResource() {
return resource;
}
public IProblemReporter getProblemReporter() {
return sessionReporter;
}
}

View file

@ -13,12 +13,9 @@ package org.eclipse.cdt.codan.internal.core;
import java.util.Map;
import org.eclipse.cdt.codan.core.CodanCorePlugin;
import org.eclipse.cdt.codan.core.CodanRuntime;
import org.eclipse.cdt.codan.core.Messages;
import org.eclipse.cdt.codan.core.model.IChecker;
import org.eclipse.cdt.codan.core.model.ICodanBuilder;
import org.eclipse.cdt.codan.core.model.IProblemReporter;
import org.eclipse.cdt.codan.core.model.IProblemReporterPersistent;
import org.eclipse.cdt.codan.core.model.IRunnableInEditorChecker;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IProject;
@ -115,31 +112,35 @@ public class CodanBuilder extends IncrementalProjectBuilder implements
monitor.beginTask(Messages.CodanBuilder_Code_Analysis_On + resource,
checkers + memsize * tick);
try {
IProblemReporter problemReporter = CodanRuntime.getInstance()
.getProblemReporter();
for (IChecker checker : chegistry) {
try {
if (monitor.isCanceled())
return;
if (checker.enabledInContext(resource)) {
// delete markers if checker can possibly run on this
// resource
// this way if checker is not enabled markers would be
// deleted too
if (problemReporter instanceof IProblemReporterPersistent) {
// delete general markers
((IProblemReporterPersistent) problemReporter)
.deleteProblems(resource, checker);
}
if (chegistry.isCheckerEnabled(checker, resource)) {
if (inEditor) {
if (checker.runInEditor()
&& checker instanceof IRunnableInEditorChecker) {
((IRunnableInEditorChecker) checker)
.processModel(model);
synchronized (checker) {
try {
checker.before(resource);
if (chegistry.isCheckerEnabled(checker,
resource)) {
//long time = System.currentTimeMillis();
if (inEditor) {
if (checker.runInEditor()
&& checker instanceof IRunnableInEditorChecker) {
((IRunnableInEditorChecker) checker)
.processModel(model);
}
} else {
checker.processResource(resource);
}
// System.err
// .println("Checker "
// + checker.getClass()
// + " worked "
// + (System
// .currentTimeMillis() - time));
}
} else {
checker.processResource(resource);
} finally {
checker.after(resource);
}
}
}

View file

@ -10,6 +10,8 @@
*******************************************************************************/
package org.eclipse.cdt.codan.internal.core.model;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
@ -20,7 +22,9 @@ import org.eclipse.cdt.codan.core.model.IChecker;
import org.eclipse.cdt.codan.core.model.ICheckersRegistry;
import org.eclipse.cdt.codan.core.model.ICodanProblemMarker;
import org.eclipse.cdt.codan.core.model.IProblem;
import org.eclipse.cdt.codan.core.model.IProblemLocation;
import org.eclipse.cdt.codan.core.model.IProblemReporterPersistent;
import org.eclipse.cdt.codan.core.model.IProblemReporterSessionPersistent;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
@ -33,10 +37,45 @@ import org.eclipse.core.runtime.IProgressMonitor;
* Problem reported that created eclipse markers
*/
public class CodanMarkerProblemReporter extends AbstractProblemReporter
implements IProblemReporterPersistent {
implements IProblemReporterPersistent,
IProblemReporterSessionPersistent {
private IResource resource;
private IChecker checker;
private ArrayList<ICodanProblemMarker> toAdd = new ArrayList<ICodanProblemMarker>();
/**
* Create instance, which can be use as factory for
* IProblemReporterSessionPersistent or
* as IProblemReporterPersistent.
*/
public CodanMarkerProblemReporter() {
super();
}
/**
* @param resource
* @param checker
*/
public CodanMarkerProblemReporter(IResource resource, IChecker checker) {
this.resource = resource;
this.checker = checker;
}
public IResource getResource() {
return resource;
}
public IChecker getChecker() {
return checker;
}
@Override
protected void reportProblem(ICodanProblemMarker codanProblemMarker) {
createProblem(codanProblemMarker);
if (checker == null) {
createProblem(codanProblemMarker);
} else {
toAdd.add(codanProblemMarker);
}
}
/**
@ -44,28 +83,6 @@ public class CodanMarkerProblemReporter extends AbstractProblemReporter
*/
protected IMarker createProblem(ICodanProblemMarker codanProblemMarker) {
try {
// Do not put in duplicates
IMarker[] cur = codanProblemMarker.getResource().findMarkers(
codanProblemMarker.getProblem().getMarkerType(), false,
IResource.DEPTH_ZERO);
if (cur != null) {
String message = codanProblemMarker.createMessage();
for (IMarker element : cur) {
int line = ((Integer) element
.getAttribute(IMarker.LINE_NUMBER)).intValue();
if (line == codanProblemMarker.getLocation()
.getLineNumber()) {
String mesg = (String) element
.getAttribute(IMarker.MESSAGE);
int sev = ((Integer) element
.getAttribute(IMarker.SEVERITY)).intValue();
if (sev == codanProblemMarker.getProblem()
.getSeverity().intValue()
&& mesg.equals(message))
return element;
}
}
}
return codanProblemMarker.createMarker();
} catch (CoreException e) {
CodanCorePlugin.log(e);
@ -94,33 +111,16 @@ public class CodanMarkerProblemReporter extends AbstractProblemReporter
}
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.cdt.codan.core.model.IProblemReporterPersistent#deleteProblems
* (org.eclipse.core.resources.IResource,
* org.eclipse.cdt.codan.core.model.IChecker)
*/
public void deleteProblems(final IResource file, final IChecker checker) {
try {
ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() {
public void run(IProgressMonitor monitor) throws CoreException {
IMarker[] markers = file.findMarkers(
GENERIC_CODE_ANALYSIS_MARKER_TYPE, true,
IResource.DEPTH_INFINITE);
ICheckersRegistry reg = CodanRuntime.getInstance()
.getCheckersRegistry();
for (int i = 0; i < markers.length; i++) {
IMarker m = markers[i];
String id = m.getAttribute(IMarker.PROBLEM, ""); //$NON-NLS-1$
Collection<IProblem> problems = reg.getRefProblems(checker);
for (Iterator<IProblem> iterator = problems.iterator(); iterator
.hasNext();) {
IProblem iProblem = iterator.next();
if (iProblem.getId().equals(id))
m.delete();
}
Collection<IMarker> markers = findResourceMarkers(file,
checker);
for (Iterator<IMarker> iterator = markers.iterator(); iterator
.hasNext();) {
IMarker iMarker = iterator.next();
iMarker.delete();
}
}
}, null, IWorkspace.AVOID_UPDATE, null);
@ -128,4 +128,159 @@ public class CodanMarkerProblemReporter extends AbstractProblemReporter
CodanCorePlugin.log(e);
}
}
protected Collection<IMarker> findResourceMarkers(IResource resource,
IChecker checker) throws CoreException {
Collection<IMarker> res = new ArrayList<IMarker>();
IMarker[] markers = resource.findMarkers(
GENERIC_CODE_ANALYSIS_MARKER_TYPE, true,
IResource.DEPTH_INFINITE);
ICheckersRegistry reg = CodanRuntime.getInstance()
.getCheckersRegistry();
Collection<IProblem> problems = reg.getRefProblems(checker);
for (int i = 0; i < markers.length; i++) {
IMarker m = markers[i];
String id = m.getAttribute(IMarker.PROBLEM, ""); //$NON-NLS-1$
for (Iterator<IProblem> iterator = problems.iterator(); iterator
.hasNext();) {
IProblem iProblem = iterator.next();
if (iProblem.getId().equals(id)) {
res.add(m);
}
}
}
return res;
}
/**
* @param resource
* @param checker
* @return session aware problem reporter
* @since 1.1
*/
public IProblemReporterSessionPersistent createReporter(IResource resource,
IChecker checker) {
return new CodanMarkerProblemReporter(resource, checker);
}
public void start() {
if (checker == null)
deleteProblems(false);
}
public void done() {
if (checker != null) {
if (toAdd.size() == 0)
deleteProblems(false);
else
reconcileMarkers();
toAdd.clear();
}
}
protected void reconcileMarkers() {
try {
ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() {
public void run(IProgressMonitor monitor) throws CoreException {
Collection<IMarker> markers = findResourceMarkers(resource,
checker);
for (Iterator<IMarker> iterator = markers.iterator(); iterator
.hasNext();) {
IMarker m = iterator.next();
ICodanProblemMarker cm = similarMarker(m);
if (cm == null) {
m.delete();
} else {
updateMarker(m, cm);
toAdd.remove(cm);
}
}
for (Iterator<ICodanProblemMarker> iterator = toAdd
.iterator(); iterator.hasNext();) {
ICodanProblemMarker cm = iterator.next();
cm.createMarker();
}
}
}, null, IWorkspace.AVOID_UPDATE, null);
} catch (CoreException e) {
CodanCorePlugin.log(e);
}
}
/**
* @param m
* @param cm
*/
protected void updateMarker(IMarker m, ICodanProblemMarker cm) {
IProblemLocation loc = cm.getLocation();
try {
if (m.getAttribute(IMarker.LINE_NUMBER, 0) != loc.getLineNumber())
m.setAttribute(IMarker.LINE_NUMBER, loc.getLineNumber());
if (m.getAttribute(IMarker.CHAR_START, 0) != loc.getStartingChar())
m.setAttribute(IMarker.CHAR_START, loc.getStartingChar());
if (m.getAttribute(IMarker.CHAR_END, 0) != loc.getEndingChar())
m.setAttribute(IMarker.CHAR_END, loc.getEndingChar());
} catch (CoreException e) {
try {
m.delete();
cm.createMarker();
} catch (CoreException e1) {
CodanCorePlugin.log(e1);
}
}
}
/**
* @param m
* @return
*/
protected ICodanProblemMarker similarMarker(IMarker m) {
ICodanProblemMarker mcm = CodanProblemMarker
.createCodanProblemMarkerFromResourceMarker(m);
ArrayList<ICodanProblemMarker> cand = new ArrayList<ICodanProblemMarker>();
for (Iterator<ICodanProblemMarker> iterator = toAdd.iterator(); iterator
.hasNext();) {
ICodanProblemMarker cm = iterator.next();
if (mcm.equals(cm))
return cm;
if (similarTo(mcm, cm)) {
cand.add(cm);
}
}
if (cand.size() == 1)
return cand.get(0);
return null;
}
/**
* @param mcm
* @param cm
* @return
*/
private boolean similarTo(ICodanProblemMarker mcm, ICodanProblemMarker other) {
if (!mcm.getProblem().getId().equals(other.getProblem().getId()))
return false;
if (!Arrays.equals(mcm.getArgs(), other.getArgs()))
return false;
IProblemLocation loc1 = mcm.getLocation();
IProblemLocation loc2 = other.getLocation();
if (!loc1.getFile().equals(loc2.getFile()))
return false;
if (Math.abs(loc1.getLineNumber() - loc2.getLineNumber()) > 2)
return false;
return true;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.cdt.codan.core.model.IProblemReporterSessionPersistent#
* deleteProblems(boolean)
*/
public void deleteProblems(boolean all) {
if (all == false)
deleteProblems(resource, checker);
else
throw new UnsupportedOperationException();
}
}

View file

@ -14,6 +14,7 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Properties;
import org.eclipse.cdt.codan.core.model.CodanSeverity;
@ -37,6 +38,10 @@ public class CodanProblemMarker implements ICodanProblemMarker {
private IProblem problem;
private Object args[];
public Object[] getArgs() {
return args;
}
/**
* @param problem
* @param loc
@ -266,4 +271,30 @@ public class CodanProblemMarker implements ICodanProblemMarker {
String propArgs = serializeArgs(args);
marker.setAttribute(PROBLEM_ARGS, propArgs);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(args);
result = prime * result + ((loc == null) ? 0 : loc.hashCode());
result = prime * result + problem.getId().hashCode();
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof ICodanProblemMarker))
return false;
CodanProblemMarker other = (CodanProblemMarker) obj;
if (!Arrays.equals(args, other.args))
return false;
if (!loc.equals(other.loc))
return false;
if (!problem.getId().equals(other.problem.getId()))
return false;
return true;
}
}