/*
* Copyright (c) 2013, 2014 QNX Software Systems 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
*/
package org.eclipse.cdt.internal.ui.editor;
import java.util.Arrays;
import org.eclipse.core.expressions.EvaluationContext;
import org.eclipse.core.expressions.EvaluationResult;
import org.eclipse.core.expressions.Expression;
import org.eclipse.core.expressions.ExpressionConverter;
import org.eclipse.core.expressions.ExpressionTagNames;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.jface.resource.DataFormatException;
import org.eclipse.jface.resource.StringConverter;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.ILanguage;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.text.ISemanticHighlighter;
import org.eclipse.cdt.ui.text.ISemanticToken;
public class ContributedSemanticHighlighting extends SemanticHighlightingWithOwnPreference {
/**
* The configuration element needs to be cached until the class is instantiated. Instantiation is deferred
* to avoid loading the contributing plugin when the highlighter is not actually needed.
*/
private IConfigurationElement configurationElement;
private Boolean fStatus = null;
private ISemanticHighlighter semanticHighlighter;
private final Expression enablementExpression;
private final int priority;
private final String id;
private final String preferenceKey;
private final String displayName;
private final RGB defaultTextColor;
private final boolean defaultBold;
private final boolean defaultItalic;
private final boolean defaultStrikethrough;
private final boolean defaultUnderline;
private final boolean defaultEnabled;
private static final String Attr_Class = "class"; //$NON-NLS-1$
private static final String Attr_Priority = "priority"; //$NON-NLS-1$
private static final String Attr_PrefKey = "preferenceKey"; //$NON-NLS-1$
private static final String Attr_DisplayName = "displayName"; //$NON-NLS-1$
private static final String Attr_DefaultTextColor = "defaultTextColor"; //$NON-NLS-1$
private static final String Attr_DefaultBold = "defaultBold"; //$NON-NLS-1$
private static final String Attr_DefaultItalic = "defaultItalic"; //$NON-NLS-1$
private static final String Attr_DefaultStrikethrough = "defaultStrikethrough"; //$NON-NLS-1$
private static final String Attr_DefaultUnderline = "defaultUnderline"; //$NON-NLS-1$
private static final String Attr_DefaultEnabled = "defaultEnabled"; //$NON-NLS-1$
private static final String Var_projectNature = "projectNatures"; //$NON-NLS-1$
private static final String Var_languageId = "languageId"; //$NON-NLS-1$
private static final int Default_Priority = 1000;
public ContributedSemanticHighlighting(IConfigurationElement element) {
configurationElement = element;
// required
id = element.getDeclaringExtension().getNamespaceIdentifier() + '.'
+ element.getDeclaringExtension().getSimpleIdentifier();
int pri = Default_Priority;
String priStr = element.getAttribute(Attr_Priority);
if (priStr != null)
try {
pri = Integer.parseInt(priStr);
} catch (NumberFormatException e) {
CUIPlugin.log("Error in priority attribute of " + id + " was " + priStr, e); //$NON-NLS-1$ //$NON-NLS-2$
}
priority = pri;
Expression expr = null;
IConfigurationElement[] children = element.getChildren(ExpressionTagNames.ENABLEMENT);
switch (children.length) {
case 0:
fStatus = Boolean.TRUE;
break;
case 1:
try {
ExpressionConverter parser = ExpressionConverter.getDefault();
expr = parser.perform(children[0]);
} catch (CoreException e) {
CUIPlugin.log("Error in enablement expression of " + id, e); //$NON-NLS-1$
}
break;
default:
CUIPlugin.logError("Too many enablement expressions for " + id); //$NON-NLS-1$
fStatus = Boolean.FALSE;
break;
}
enablementExpression = expr;
preferenceKey = element.getAttribute(Attr_PrefKey);
displayName = element.getAttribute(Attr_DisplayName);
// optional
defaultTextColor = getRGBAttribute(element, id, Attr_DefaultTextColor);
defaultBold = Boolean.parseBoolean(element.getAttribute(Attr_DefaultBold));
defaultItalic = Boolean.parseBoolean(element.getAttribute(Attr_DefaultItalic));
defaultStrikethrough = Boolean.parseBoolean(element.getAttribute(Attr_DefaultStrikethrough));
defaultUnderline = Boolean.parseBoolean(element.getAttribute(Attr_DefaultUnderline));
defaultEnabled = Boolean.parseBoolean(element.getAttribute(Attr_DefaultEnabled));
}
public String getId() {
return id;
}
public int getPriority() {
return priority;
}
private static RGB getRGBAttribute(IConfigurationElement element, String extensionId, String key) {
String val = element.getAttribute(key);
if (val != null)
try {
return StringConverter.asRGB(val);
} catch (DataFormatException e) {
CUIPlugin
.log("Error in " + Attr_DefaultTextColor + " attribute of " + extensionId + ' ' + val + " is not a RGB value", e); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
// black by default
return new RGB(0, 0, 0);
}
private ISemanticHighlighter createSemanticHighlighter() {
// only one try at creating the class
if (configurationElement == null)
return null;
IConfigurationElement element = configurationElement;
configurationElement = null;
try {
return (ISemanticHighlighter) element.createExecutableExtension(Attr_Class);
} catch (CoreException e) {
CUIPlugin.log("Error in class attribute of " + id, e); //$NON-NLS-1$
}
return null;
}
@Override
public String getPreferenceKey() {
return preferenceKey;
}
@Override
public RGB getDefaultDefaultTextColor() {
return defaultTextColor;
}
@Override
public boolean isBoldByDefault() {
return defaultBold;
}
@Override
public boolean isItalicByDefault() {
return defaultItalic;
}
@Override
public boolean isStrikethroughByDefault() {
return defaultStrikethrough;
}
@Override
public boolean isUnderlineByDefault() {
return defaultUnderline;
}
@Override
public boolean isEnabledByDefault() {
return defaultEnabled;
}
@Override
public String getDisplayName() {
return displayName;
}
@Override
public boolean requiresImplicitNames() {
return false;
}
private boolean matches(ITranslationUnit tu) {
// if the enablement expression is missing or structurally invalid, then return immediately
if (fStatus != null)
return fStatus.booleanValue();
if (enablementExpression != null)
try {
EvaluationContext evalContext = new EvaluationContext(null, tu);
ICProject cProject = tu.getCProject();
String[] natures = cProject.getProject().getDescription().getNatureIds();
evalContext.addVariable(Var_projectNature, Arrays.asList(natures));
ILanguage language = tu.getLanguage();
if( language != null )
evalContext.addVariable(Var_languageId, language.getId());
return enablementExpression.evaluate(evalContext) == EvaluationResult.TRUE;
} catch (CoreException e) {
CUIPlugin.log("Error while evaluating enablement expression for " + id, e); //$NON-NLS-1$
}
fStatus = Boolean.FALSE;
return false;
}
/**
* Return the contributed ISemanticHighlighter if the receiver should be applied to the specified TU and
* null otherwise.
*/
private ISemanticHighlighter getSemanticHighlighter(ITranslationUnit tu) {
if (!matches(tu))
return null;
if (semanticHighlighter == null)
synchronized (this) {
if (semanticHighlighter == null) {
semanticHighlighter = createSemanticHighlighter();
}
}
return semanticHighlighter;
}
@Override
public boolean consumes(ISemanticToken token) {
if (token == null)
return false;
IASTTranslationUnit astTU = token.getRoot();
if (astTU == null)
return false;
ITranslationUnit tu = astTU.getOriginatingTranslationUnit();
if (tu == null)
return false;
ISemanticHighlighter highlighter = getSemanticHighlighter(tu);
if (highlighter == null)
return false;
return highlighter.consumes(token);
}
}