/**
* Copyright (c) 2009-2011, The HATS Consortium. All rights reserved.
* This file is licensed under the terms of the Modified BSD License.
*/
package org.absmodels.abs.plugin.decorators;
import static org.absmodels.abs.plugin.util.Constants.MARKER_TYPE;
import static org.absmodels.abs.plugin.util.Constants.MODULE_DECORATOR_ID;
import static org.absmodels.abs.plugin.util.Images.ERROR_MARKER;
import java.util.List;
import org.absmodels.abs.plugin.Activator;
import org.absmodels.abs.plugin.builder.AbsNature;
import org.absmodels.abs.plugin.navigator.ModulePath;
import org.absmodels.abs.plugin.util.InternalASTNode;
import org.absmodels.abs.plugin.util.UtilityFunctions;
import org.absmodels.abs.plugin.util.UtilityFunctions.EditorPosition;
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.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.DecorationOverlayIcon;
import org.eclipse.jface.viewers.IDecoration;
import org.eclipse.jface.viewers.ILightweightLabelDecorator;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.LabelProviderChangedEvent;
import org.eclipse.ui.PlatformUI;
import abs.frontend.analyser.SemanticCondition;
import abs.frontend.analyser.SemanticConditionList;
import abs.frontend.ast.ASTNode;
import abs.frontend.ast.CompilationUnit;
import abs.frontend.ast.Model;
import abs.frontend.ast.ModuleDecl;
import abs.frontend.parser.ParserError;
/**
* Class for decorating {@link ModuleDecl} and {@link ModulePath} instances with
* error markers
*
* @author cseise
*
*/
public class ModuleDecorator extends LabelProvider implements ILightweightLabelDecorator {
private final static ImageDescriptor ERROR_MARKER_DESCRIPTOR = DecorationOverlayIcon.createFromImage(ERROR_MARKER);
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
@Override
public void decorate(Object element, IDecoration decoration) {
if (element instanceof InternalASTNode<?>) {
InternalASTNode<?> node = (InternalASTNode<?>) element;
if (node.hasASTNodeOfType(ModuleDecl.class)){
checkModuleDecl(decoration, (InternalASTNode<ModuleDecl>) node);
}
} else if (element instanceof ModulePath) {
ModulePath m = (ModulePath) element;
checkModulePath(decoration, m);
} else if (element instanceof IProject) {
IProject project = (IProject) element;
checkProject(project, decoration);
}
}
private void checkProject(IProject project, IDecoration decoration) {
try {
if (project.isAccessible()) {
IMarker[] markers = getABSErrorMarkers(project);
if (markers.length > 0) {
addErrorOverlay(decoration);
}
}
} catch (CoreException e) {
Activator.logException(e);
}
}
private void checkModulePath(IDecoration decoration, ModulePath m) {
AbsNature nature = m.getNature();
Model model = nature.getCompleteModel();
if (model != null) {
for (ModuleDecl mod : model.getModuleDecls()) {
if (mod.getName().startsWith(m.getModulePath()+".")){
if (hasModuleDeclErrors(mod, nature)) {
addErrorOverlay(decoration);
return;
}
}
}
}
}
private void checkModuleDecl(IDecoration decoration, InternalASTNode<ModuleDecl> modDecl) {
AbsNature nature = modDecl.getNature();
if (nature != null && hasModuleDeclErrors(modDecl.getASTNode(), modDecl.getNature())) {
addErrorOverlay(decoration);
}
}
private IMarker[] getABSErrorMarkers(Object element) throws CoreException {
if (element instanceof IProject) {
IProject project = (IProject) element;
IMarker[] markers = project.findMarkers(MARKER_TYPE, true, IResource.DEPTH_INFINITE);
return markers;
}else{
return new IMarker[0];
}
}
private void addErrorOverlay(IDecoration decoration) {
decoration.addOverlay(ERROR_MARKER_DESCRIPTOR, IDecoration.BOTTOM_LEFT);
}
/**
* Determines if a module declaration has any errors
*
* @param m
* the module declaration
* @param nature
* the ABS nature
* @return TRUE if the module declaration has errors, FALSE if not or m or
* nature is null
*/
public boolean hasModuleDeclErrors(ModuleDecl m, AbsNature nature) {
synchronized (nature.modelLock) {
if (m != null) {
CompilationUnit cu = m.getCompilationUnit();
EditorPosition pos = UtilityFunctions.getPosition(m);
int startLine = pos.getLinestart();
int endLine = pos.getLineend();
List<ParserError> parserErrors = cu.getParserErrors();
SemanticConditionList list = cu.getModel().getTypeErrors();
if (checkParserErrorRange(startLine, endLine, parserErrors)){
return true;
}else{
return checkSemanticErrorRange(list,cu,startLine,endLine,nature);
}
}
return false;
}
}
/**
* Fires a LabelProviderChangeEvent to force re-decoration.
* This is a workaround for circumventing decorator update problems.
*/
public void refresh(){
boolean isEnabled = PlatformUI.getWorkbench().getDecoratorManager().getEnabled(MODULE_DECORATOR_ID);
if (isEnabled){
this.fireLabelProviderChanged(new LabelProviderChangedEvent(this));
}
}
private boolean checkSemanticErrorRange(SemanticConditionList list, CompilationUnit c, int startLine, int endLine, AbsNature nature) {
synchronized (nature.modelLock) {
if (list != null) {
if (list.containsErrors()) {
for (SemanticCondition err : list) {
ASTNode<?> node = err.getNode();
int line = err.getLine();
CompilationUnit cu = node.getCompilationUnit();
if (c == cu && checkLine(line, startLine, endLine)) {
return true;
}
}
}
}
}
return false;
}
private boolean checkParserErrorRange(int startLine, int endLine, List<ParserError> parserErrors) {
if (parserErrors.size() > 0) {
for (ParserError err : parserErrors) {
int line = err.getLine();
return checkLine(line, startLine, endLine);
}
}
return false;
}
private boolean checkLine(int line, int startLine, int endLine) {
return startLine <= line && endLine >= line;
}
}