blob: c3eec9fdb8a68099fb71bc861283bdfc54651d56 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2007 IBM Corporation and others. 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: Sian January - initial version
* Matt Chapman - add source of advice markers
******************************************************************************/
package org.eclipse.ajdt.internal.ui.markers;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.aspectj.asm.IProgramElement;
import org.aspectj.asm.IRelationship;
import org.aspectj.weaver.AdviceKind;
import org.aspectj.weaver.model.AsmRelationshipProvider;
import org.eclipse.ajdt.core.AJLog;
import org.eclipse.ajdt.core.model.AJProjectModelFacade;
import org.eclipse.ajdt.core.model.AJProjectModelFactory;
import org.eclipse.ajdt.internal.ui.preferences.AspectJPreferences;
import org.eclipse.ajdt.internal.ui.text.UIMessages;
import org.eclipse.ajdt.ui.IAJModelMarker;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
/**
* Class responsible for advice and declaration markers. Updates the markers for
* a given project when it is built.
*/
public class UpdateAJMarkers {
private final AJProjectModelFacade model;
private final IProject project;
private final IFile[] sourceFiles;
private int fileCount;
private int markerCount;
/**
* To update markers for the entire project
*
* @param project the poject that needs updating
*/
public UpdateAJMarkers(IProject project) {
this.model = AJProjectModelFactory.getInstance().getModelForProject(project);
this.project = project;
this.sourceFiles = null;
this.fileCount = 0;
this.markerCount = 0;
}
/**
* to update markers for the given files only.
*
* @param project the poject that needs updating
*
* @param sourceFiles List of Strings of absolute paths of files that
* need updating
*
* @deprecated Use {@link UpdateAJMarkers#UpdateAJMarkers(IProject, IFile[])} instead
*/
public UpdateAJMarkers(IProject project, File[] sourceFiles) {
this.model = AJProjectModelFactory.getInstance().getModelForProject(project);
this.project = project;
this.sourceFiles = DeleteAndUpdateAJMarkersJob.javaFileToIFile(sourceFiles, project);
}
/**
* to update markers for the given files only.
*
* @param project the poject that needs updating
*
* @param sourceFiles List of Strings of absolute paths of files that
* need updating
*
*/
public UpdateAJMarkers(IProject project, IFile[] sourceFiles) {
this.model = AJProjectModelFactory.getInstance().getModelForProject(project);
this.project = project;
this.sourceFiles = sourceFiles;
}
protected IStatus run(IProgressMonitor monitor) {
AJLog.logStart("Create markers: " + project.getName());
if (sourceFiles != null) {
addMarkersForFiles(monitor);
} else {
addMarkersForProject(monitor);
}
AJLog.logEnd(AJLog.BUILDER, "Create markers: " + project.getName(), "Finished creating markers for " + project.getName());
AJLog.log(AJLog.BUILDER, "Created " + markerCount + " markers in " + fileCount + " files");
return Status.OK_STATUS;
}
/**
* creates new markers for an entire project
*/
private void addMarkersForProject(IProgressMonitor monitor) {
if (! model.hasModel()) {
return;
}
try {
IJavaProject jProject = JavaCore.create(project);
IPackageFragmentRoot[] fragRoots = jProject.getPackageFragmentRoots();
SubProgressMonitor subMonitor = new SubProgressMonitor(monitor, fragRoots.length);
subMonitor.beginTask("Add markers for " + project.getName(), fragRoots.length);
for (int i = 0; i < fragRoots.length; i++) {
if (fragRoots[i].getKind() == IPackageFragmentRoot.K_SOURCE) {
IJavaElement[] frags = fragRoots[i].getChildren();
for (int j = 0; j < frags.length; j++) {
Set<String> completedCUNames = new HashSet<String>(frags.length, 1.0f);
IJavaElement[] cus = ((IPackageFragment) frags[j]).getChildren();
for (int k = 0; k < cus.length; k++) {
// ignore any class files in the source folder (Bug 258698)
if (cus[k].getElementType() == IJavaElement.COMPILATION_UNIT) {
// ignore duplicate compilation units
IResource resource = cus[k].getResource();
if (!completedCUNames.contains(resource.getName())) {
subMonitor.subTask("Add markers for " + cus[k].getElementName());
addMarkersForFile((ICompilationUnit) cus[k], ((ICompilationUnit) cus[k]).getResource());
completedCUNames.add(resource.getName());
fileCount++;
}
if (subMonitor.isCanceled()) {
throw new OperationCanceledException();
}
}
}
}
subMonitor.worked(1);
}
}
} catch (JavaModelException e) {
}
}
private void addMarkersForFiles(IProgressMonitor monitor) {
SubProgressMonitor subMonitor = new SubProgressMonitor(monitor, sourceFiles.length);
for (int i = 0; i < sourceFiles.length; i++) {
IJavaElement unit = JavaCore.create(sourceFiles[i]);
if (unit != null && unit.exists() && unit instanceof ICompilationUnit) {
subMonitor.subTask("Add markers for " + unit.getElementName());
addMarkersForFile((ICompilationUnit) unit, sourceFiles[i]);
fileCount++;
}
if (subMonitor.isCanceled()) {
throw new OperationCanceledException();
}
subMonitor.worked(1);
}
}
private void addMarkersForFile(ICompilationUnit cu, IResource resource) {
Map<Integer,List<IRelationship>> annotationMap =
model.getRelationshipsForFile(cu);
for (Entry<Integer,List<IRelationship>> entry : annotationMap.entrySet()) {
createMarker(resource, entry.getKey().intValue(), entry.getValue());
markerCount++;
}
}
private void createMarker(IResource resource, int lineNumber, List<IRelationship> relationships) {
String markerType = null;
boolean hasRuntime = false;
for (IRelationship relationship : relationships) {
hasRuntime |= relationship.hasRuntimeTest();
String customMarkerType = getCustomMarker(relationship);
if (customMarkerType != null) {
// Create a marker of the saved type or don't create one.
// user has configured a custom marker
createCustomMarker(resource, customMarkerType, lineNumber, relationship);
} else {
// must repeat for each target since
// each target may be of a different type
List<String> targets = relationship.getTargets();
for (String target : targets) {
String markerTypeForRelationship =
getMarkerTypeForRelationship(relationship, target);
if (markerTypeForRelationship != null) {
if (markerType == null) {
markerType = markerTypeForRelationship;
} else if (!markerType.equals(markerTypeForRelationship)) {
markerType = getCombinedMarkerType(markerType,
markerTypeForRelationship, hasRuntime);
}
}
}
}
} // end for
// Create the marker
if (markerType != null) {
try {
IMarker marker = resource.createMarker(markerType);
marker.setAttribute(IMarker.LINE_NUMBER, lineNumber);
String label;
int numTargets = getNumTargets(relationships);
if (numTargets == 1) {
label = getMarkerLabel(relationships);
} else {
label = getMultipleMarkersLabel(numTargets);
}
marker.setAttribute(IMarker.MESSAGE, label);
marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH);
} catch (CoreException e) {
}
}
}
private int getNumTargets(List<IRelationship> relationships) {
int numTargets = 0;
for (IRelationship rel : relationships) {
for (String target : rel.getTargets()) {
if (!rel.getName().equals(AsmRelationshipProvider.MATCHES_DECLARE)) {
numTargets++;
}
}
}
return numTargets;
}
private void createCustomMarker(IResource resource, String customMarkerType,
int lineNumber, IRelationship relationship) {
if (! customMarkerType.equals(AJMarkersDialog.NO_MARKERS)) {
try {
IMarker marker = resource
.createMarker(IAJModelMarker.CUSTOM_MARKER);
marker.setAttribute(IMarker.LINE_NUMBER, lineNumber);
String label;
label = getMarkerLabel(relationship);
marker.setAttribute(IMarker.MESSAGE, label);
marker.setAttribute(IMarker.PRIORITY,
IMarker.PRIORITY_HIGH);
marker.setAttribute(
CustomMarkerImageProvider.IMAGE_LOCATION_ATTRIBUTE,
customMarkerType);
} catch (CoreException e) {
}
}
}
/**
* Get the marker type that should be used for the given relationship
*
* @param relationship
* @param target
* @return
*/
private String getMarkerTypeForRelationship(IRelationship relationship, String target) {
String name = relationship.getName();
boolean runtimeTest = relationship.hasRuntimeTest();
String markerType;
if (name.equals(AsmRelationshipProvider.ADVISED_BY)) {
IProgramElement advice = model.getProgramElement(target);
AdviceKind ak;
if (advice.getExtraInfo() != null) {
ak = AdviceKind.stringToKind(advice.getExtraInfo().getExtraAdviceInformation());
} else {
ak = null;
}
if (runtimeTest) {
if (ak == null) {
markerType = IAJModelMarker.DYNAMIC_ADVICE_MARKER;
} else if (ak == AdviceKind.Before) {
markerType = IAJModelMarker.DYNAMIC_BEFORE_ADVICE_MARKER;
} else if (ak == AdviceKind.After ||
ak == AdviceKind.AfterReturning ||
ak == AdviceKind.AfterThrowing) {
markerType = IAJModelMarker.DYNAMIC_AFTER_ADVICE_MARKER;
} else if (ak == AdviceKind.Around) {
markerType = IAJModelMarker.DYNAMIC_AROUND_ADVICE_MARKER;
} else {
markerType = IAJModelMarker.DYNAMIC_ADVICE_MARKER;
}
} else { // no runtime test
if (ak == null) {
markerType = IAJModelMarker.ADVICE_MARKER;
} else if (ak == AdviceKind.Before) {
markerType = IAJModelMarker.BEFORE_ADVICE_MARKER;
} else if (ak == AdviceKind.After ||
ak == AdviceKind.AfterReturning ||
ak == AdviceKind.AfterThrowing) {
markerType = IAJModelMarker.AFTER_ADVICE_MARKER;
} else if (ak == AdviceKind.Around) {
markerType = IAJModelMarker.AROUND_ADVICE_MARKER;
} else {
markerType = IAJModelMarker.ADVICE_MARKER;
}
}
} else if (name.equals(AsmRelationshipProvider.ADVISES)) {
IProgramElement advice = model.getProgramElement(relationship.getSourceHandle());
AdviceKind ak;
if (advice.getExtraInfo() != null) {
ak = AdviceKind.stringToKind(advice.getExtraInfo().getExtraAdviceInformation());
} else {
ak = null;
// hmmm...sometmes ExtradviceInformtion is null.
// try to get the advice kind by the name
if (advice.getName().startsWith("before")) {
ak = AdviceKind.Before;
} else if (advice.getName().startsWith("after")) {
ak = AdviceKind.After;
} else if (advice.getName().startsWith("around")) {
ak = AdviceKind.Around;
}
}
if (runtimeTest) {
if (ak == null) {
markerType = IAJModelMarker.SOURCE_DYNAMIC_ADVICE_MARKER;
} else if (ak == AdviceKind.Before) {
markerType = IAJModelMarker.SOURCE_DYNAMIC_BEFORE_ADVICE_MARKER;
} else if (ak == AdviceKind.After ||
ak == AdviceKind.AfterReturning ||
ak == AdviceKind.AfterThrowing) {
markerType = IAJModelMarker.SOURCE_DYNAMIC_AFTER_ADVICE_MARKER;
} else if (ak == AdviceKind.Around) {
markerType = IAJModelMarker.SOURCE_DYNAMIC_AROUND_ADVICE_MARKER;
} else {
markerType = IAJModelMarker.SOURCE_DYNAMIC_ADVICE_MARKER;
}
} else { // no runtime test
if (ak == null) {
markerType = IAJModelMarker.SOURCE_ADVICE_MARKER;
} else if (ak == AdviceKind.Before) {
markerType = IAJModelMarker.SOURCE_BEFORE_ADVICE_MARKER;
} else if (ak == AdviceKind.After ||
ak == AdviceKind.AfterReturning ||
ak == AdviceKind.AfterThrowing) {
markerType = IAJModelMarker.SOURCE_AFTER_ADVICE_MARKER;
} else if (ak == AdviceKind.Around) {
markerType = IAJModelMarker.SOURCE_AROUND_ADVICE_MARKER;
} else {
markerType = IAJModelMarker.SOURCE_ADVICE_MARKER;
}
}
} else if (name.equals(AsmRelationshipProvider.ANNOTATED_BY) ||
name.equals(AsmRelationshipProvider.SOFTENED_BY) ||
name.equals(AsmRelationshipProvider.INTER_TYPE_DECLARED_BY)) {
// note that we ignore MATCHES_DECLARE because that is taken care of
// by error and warning markers
markerType = IAJModelMarker.ITD_MARKER;
} else if (name.equals(AsmRelationshipProvider.ANNOTATES) ||
name.equals(AsmRelationshipProvider.INTER_TYPE_DECLARES) ||
name.equals(AsmRelationshipProvider.SOFTENS) ||
name.equals(AsmRelationshipProvider.MATCHED_BY)) {
markerType = IAJModelMarker.SOURCE_ITD_MARKER;
} else {
markerType = null;
}
return markerType;
}
private String getMultipleMarkersLabel(int number) {
return number + " " + UIMessages.AspectJMarkersAtLine; //$NON-NLS-1$
}
private String getMarkerLabel(List<IRelationship> relationships) {
// find first non-matches declare relationship
for (IRelationship rel : relationships) {
if (!rel.getName().equals(AsmRelationshipProvider.MATCHES_DECLARE)) {
return getMarkerLabel(rel);
}
}
return "<none>";
}
/**
* Get a label for the given relationship
*
* @param relationship
* @return
*/
private String getMarkerLabel(IRelationship relationship) {
IProgramElement target = model.getProgramElement(
(String) relationship.getTargets().get(0));
return relationship.getName()
+ " " //$NON-NLS-1$
+ (target != null ? target.toLinkLabelString(false) : "null")
+ (relationship.hasRuntimeTest() ? " " + //$NON-NLS-1$
UIMessages.AspectJEditor_runtimetest
: ""); //$NON-NLS-1$
}
/**
* check if this relationship comes from an aspect that has a custom marker
* @see AJMarkersDialog
*/
private String getCustomMarker(IRelationship relationship) {
// get the element in the aspect, it is source or target depending
// on the kind of relationship
List<IJavaElement> aspectEntities = new ArrayList<IJavaElement>();
if (relationship.isAffects()) {
aspectEntities.add(model.programElementToJavaElement(relationship.getSourceHandle()));
} else {
// all targets are from the same
for (String target : relationship.getTargets()) {
aspectEntities.add(model.programElementToJavaElement(target));
}
}
for (IJavaElement elt : aspectEntities) {
if (elt != null) { // will be null if the referent is not found. Should only be in error cases
IType typeElement = (IType) elt.getAncestor(IJavaElement.TYPE);
if (typeElement != null) {
String customImage = AspectJPreferences.getSavedIcon(typeElement.getJavaProject()
.getProject(), AJMarkersDialog
.getFullyQualifiedAspectName(typeElement));
if (customImage != null) {
return customImage;
}
}
}
}
return null;
}
/**
* Two or more markers on the same line - get the most approriate marker
* type to display
*
* @param firstMarkerType
* @param secondMarkerType
* @param runtimeTest
* @return
*/
private String getCombinedMarkerType(String firstMarkerType,
String secondMarkerType, boolean runtimeTest) {
if (firstMarkerType.indexOf("source") != -1 && secondMarkerType.indexOf("source") != -1) { //$NON-NLS-1$ //$NON-NLS-2$
// around advice trumps before and after
if (firstMarkerType.indexOf("around") != -1 || secondMarkerType.indexOf("around") != -1) { //$NON-NLS-1$ //$NON-NLS-2$
return runtimeTest ? IAJModelMarker.SOURCE_DYNAMIC_AROUND_ADVICE_MARKER
: IAJModelMarker.SOURCE_AROUND_ADVICE_MARKER;
} else {
return runtimeTest ? IAJModelMarker.SOURCE_DYNAMIC_ADVICE_MARKER
: IAJModelMarker.SOURCE_ADVICE_MARKER;
}
} else if (firstMarkerType.indexOf("source") != -1 || secondMarkerType.indexOf("source") != -1) { //$NON-NLS-1$ //$NON-NLS-2$
// being source and target trumps around, before, and after
return runtimeTest ? IAJModelMarker.DYNAMIC_SOURCE_AND_TARGET_MARKER
: IAJModelMarker.SOURCE_AND_TARGET_MARKER;
} else {
// around advice trumps before and after
if (firstMarkerType.indexOf("around") != -1 || secondMarkerType.indexOf("around") != -1) { //$NON-NLS-1$ //$NON-NLS-2$
return runtimeTest ? IAJModelMarker.DYNAMIC_AROUND_ADVICE_MARKER
: IAJModelMarker.AROUND_ADVICE_MARKER;
} else {
return runtimeTest ? IAJModelMarker.DYNAMIC_ADVICE_MARKER
: IAJModelMarker.ADVICE_MARKER;
}
}
}
}