Bug 577558: [Ltk-Model] Add support for problem decoration to image of
ExtModelLabelProvider
- Add DecoratedElementImageDescriptor
Change-Id: I999452f33a36c60c9baaef7f0d99c972efb39ed8
diff --git a/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/LtkUIResources.java b/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/LtkUIResources.java
index d814ee3..735cd0d 100644
--- a/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/LtkUIResources.java
+++ b/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/LtkUIResources.java
@@ -27,6 +27,7 @@
private static final String NS= LtkUI.BUNDLE_ID;
+
public static final String OBJ_ERROR_IMAGE_ID= NS + "/image/obj/Error"; //$NON-NLS-1$
public static final String OBJ_ERROR_AWAY_IMAGE_ID= NS + "/image/obj/Error.away"; //$NON-NLS-1$
public static final String OBJ_WARNING_IMAGE_ID= NS + "/image/obj/Warning"; //$NON-NLS-1$
diff --git a/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/util/DecoratedElementImageDescriptor.java b/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/util/DecoratedElementImageDescriptor.java
new file mode 100644
index 0000000..8e710cc
--- /dev/null
+++ b/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/util/DecoratedElementImageDescriptor.java
@@ -0,0 +1,197 @@
+/*=============================================================================#
+ # Copyright (c) 2000, 2021 IBM Corporation and others.
+ #
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0.
+ #
+ # SPDX-License-Identifier: EPL-2.0
+ #
+ # Contributors:
+ # IBM Corporation - org.eclipse.jdt: initial API and implementation
+ # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.ltk.ui.util;
+
+import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert;
+
+import org.eclipse.jface.resource.CompositeImageDescriptor;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.graphics.Point;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.ecommons.ui.SharedUIResources;
+
+
+@NonNullByDefault
+public class DecoratedElementImageDescriptor extends CompositeImageDescriptor {
+
+
+ /** Flag to render the info adornment. */
+ public final static int INFO= 0b1 << 0;
+
+ /** Flag to render the warning adornment. */
+ public final static int WARNING= 0b1 << 1;
+
+ /** Flag to render the error adornment. */
+ public final static int ERROR= 0b1 << 2;
+
+
+ /**
+ * Flag to render the 'deprecated' adornment.
+ */
+ public final static int DEPRECATED= 0b1 << 4;
+
+ /**
+ * Flag to render the 'ignore optional compile problems' adornment.
+ */
+ public final static int IGNORE_OPTIONAL_PROBLEMS= 0b1 << 8;
+
+
+ private final ImageDescriptor baseImage;
+ private final int flags;
+
+ private @Nullable Point size;
+
+
+ /**
+ * Creates a new JavaElementImageDescriptor.
+ *
+ * @param baseImage an image descriptor used as the base image
+ * @param flags flags indicating which adornments are to be rendered. See {@link #setAdornments(int)}
+ * for valid values.
+ * @param size the size of the resulting image
+ */
+ public DecoratedElementImageDescriptor(final ImageDescriptor baseImage, final int flags) {
+ this.baseImage= nonNullAssert(baseImage);
+ this.flags= flags;
+ }
+
+
+ protected final ImageDescriptor getBaseImage() {
+ return this.baseImage;
+ }
+
+ protected final int getFlags() {
+ return this.flags;
+ }
+
+ @Override
+ protected final Point getSize() {
+ var size= this.size;
+ if (size == null) {
+ final var data= createCachedImageDataProvider(getBaseImage());
+ size= new Point(data.getWidth(), data.getHeight());
+ this.size= size;
+ }
+ return size;
+ }
+
+
+ @Override
+ protected void drawCompositeImage(final int width, final int height) {
+ if ((this.flags & DEPRECATED) != 0) { // draw *behind* the full image
+ final Point size= getSize();
+ final var data= createCachedImageDataProvider(
+ SharedUIResources.getInstance().getImageDescriptor(
+ SharedUIResources.OVR_DEPRECATED_IMAGE_ID ));
+ drawImage(data, 0, size.y - data.getHeight());
+ }
+
+ { final var data= createCachedImageDataProvider(getBaseImage());
+ drawImage(data, 0, 0);
+ }
+
+ drawTopRight();
+ drawBottomRight();
+ drawBottomLeft();
+ }
+
+ private void addTopRightImage(final ImageDescriptor desc, final Point pos) {
+ final var data= createCachedImageDataProvider(desc);
+ final int x= pos.x - data.getWidth();
+ if (x >= 0) {
+ drawImage(data, x, pos.y);
+ pos.x= x;
+ }
+ }
+
+ private void addBottomRightImage(final ImageDescriptor desc, final Point pos) {
+ final var data= createCachedImageDataProvider(desc);
+ final int x= pos.x - data.getWidth();
+ final int y= pos.y - data.getHeight();
+ if (x >= 0 && y >= 0) {
+ drawImage(data, x, y);
+ pos.x= x;
+ }
+ }
+
+ private void addBottomLeftImage(final ImageDescriptor desc, final Point pos) {
+ final var data= createCachedImageDataProvider(desc);
+ final int x= pos.x;
+ final int y= pos.y - data.getHeight();
+ final int xEnd= x + data.getWidth();
+ if (xEnd < getSize().x && y >= 0) {
+ drawImage(data, x, y);
+ pos.x= xEnd;
+ }
+ }
+
+
+ private void drawTopRight() {
+ }
+
+ private void drawBottomRight() {
+ }
+
+ private void drawBottomLeft() {
+ final Point pos= new Point(0, getSize().y);
+ if ((this.flags & ERROR) != 0) {
+ addBottomLeftImage(
+ SharedUIResources.getInstance().getImageDescriptor(
+ SharedUIResources.OVR_ERROR_IMAGE_ID ),
+ pos );
+ }
+ else if ((this.flags & WARNING) != 0) {
+ addBottomLeftImage(
+ SharedUIResources.getInstance().getImageDescriptor(
+ SharedUIResources.OVR_WARNING_IMAGE_ID ),
+ pos );
+ }
+ else if ((this.flags & INFO) != 0) {
+ addBottomLeftImage(
+ SharedUIResources.getInstance().getImageDescriptor(
+ SharedUIResources.OVR_INFO_IMAGE_ID ),
+ pos );
+ }
+ if ((this.flags & IGNORE_OPTIONAL_PROBLEMS) != 0) {
+ addBottomLeftImage(
+ SharedUIResources.getInstance().getImageDescriptor(
+ SharedUIResources.OVR_IGNORE_OPTIONAL_PROBLEMS_IMAGE_ID ),
+ pos );
+ }
+ }
+
+
+ @Override
+ public int hashCode() {
+ return (this.baseImage.hashCode() ^ this.flags);
+ }
+
+ @Override
+ public boolean equals(final @Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj != null && getClass() == obj.getClass()) {
+ final DecoratedElementImageDescriptor other= (DecoratedElementImageDescriptor)obj;
+ return (this.baseImage.equals(other.baseImage)
+ && this.flags == other.flags );
+ }
+ return false;
+ }
+
+}
diff --git a/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/util/ExtModelLabelProvider.java b/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/util/ExtModelLabelProvider.java
index c5d4d2b..4eed447 100644
--- a/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/util/ExtModelLabelProvider.java
+++ b/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/util/ExtModelLabelProvider.java
@@ -14,22 +14,41 @@
package org.eclipse.statet.ltk.ui.util;
+import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullElse;
+
import java.util.IdentityHashMap;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.StyledCellLabelProvider;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.statet.jcommons.lang.Disposable;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.jcommons.lang.Nullable;
+import org.eclipse.statet.jcommons.text.core.TextRegion;
+import org.eclipse.statet.ecommons.ui.SharedUIResources;
+import org.eclipse.statet.ecommons.ui.jface.resource.ImageImageDescriptor;
+
+import org.eclipse.statet.ltk.ast.core.AstNode;
+import org.eclipse.statet.ltk.ast.core.Asts;
+import org.eclipse.statet.ltk.core.Ltk;
+import org.eclipse.statet.ltk.issues.core.Issue;
+import org.eclipse.statet.ltk.issues.core.Problem;
+import org.eclipse.statet.ltk.model.core.LtkModelUtils;
import org.eclipse.statet.ltk.model.core.LtkModels;
import org.eclipse.statet.ltk.model.core.element.EmbeddingForeignElement;
import org.eclipse.statet.ltk.model.core.element.LtkModelElement;
+import org.eclipse.statet.ltk.model.core.element.SourceElement;
+import org.eclipse.statet.ltk.model.core.element.SourceStructElement;
+import org.eclipse.statet.ltk.model.core.element.SourceUnit;
import org.eclipse.statet.ltk.ui.ElementLabelProvider;
+import org.eclipse.statet.ltk.ui.sourceediting.SourceIssueEditorAnnotation;
@NonNullByDefault
@@ -45,6 +64,11 @@
private @Nullable String modelTypeId;
+ private @Nullable String documentProviderModelTypeId;
+ private @Nullable IDocumentProvider documentProvider;
+
+ private final SharedUIResources sharedResources= SharedUIResources.getInstance();
+
public ExtModelLabelProvider(final String modelTypeId) {
this.modelTypeId= modelTypeId;
@@ -124,6 +148,15 @@
image= provider.getImage(embeddedElement);
}
}
+ if (image != null) {
+ final int adornmentFlags= computeAdornmentFlags(element);
+ if (adornmentFlags != 0) {
+ final var imageDescriptor= new DecoratedElementImageDescriptor(
+ new ImageImageDescriptor(image), adornmentFlags );
+ return this.sharedResources.getImageDescriptorRegistry()
+ .get(imageDescriptor);
+ }
+ }
return image;
}
@@ -180,6 +213,104 @@
}
+ protected int computeAdornmentFlags(final LtkModelElement<?> element) {
+ int flags= 0;
+ if (element instanceof SourceElement) {
+ final var sourceElement= (SourceElement<?>)element;
+ final var sourceUnit= sourceElement.getSourceUnit();
+ if (sourceUnit != null) {
+ if (sourceUnit.getWorkingContext() == Ltk.EDITOR_CONTEXT) {
+ if (hasAstError(sourceElement)) {
+ flags|= DecoratedElementImageDescriptor.ERROR;
+ }
+ else if (isUpToDate(sourceElement)) {
+ final IAnnotationModel annotationModel= getAnnotationModel(sourceUnit);
+ if (annotationModel != null) {
+ flags|= getProblemFlag(annotationModel, sourceElement);
+ }
+ }
+ }
+ }
+ }
+ return flags;
+ }
+
+ private @Nullable IAnnotationModel getAnnotationModel(final SourceUnit sourceUnit) {
+ final String modelTypeId= nonNullElse(this.modelTypeId, sourceUnit.getModelTypeId());
+ final IDocumentProvider documentProvider;
+ if (modelTypeId == this.documentProviderModelTypeId) {
+ documentProvider= this.documentProvider;
+ }
+ else {
+ documentProvider= LtkModels.getModelAdapter(modelTypeId, IDocumentProvider.class);
+ this.documentProviderModelTypeId= modelTypeId;
+ this.documentProvider= documentProvider;
+ }
+ if (documentProvider == null) {
+ return null;
+ }
+ return documentProvider.getAnnotationModel(sourceUnit);
+ }
+
+ private boolean isUpToDate(final SourceElement<?> element) {
+ if (element instanceof SourceStructElement) {
+ final var containerElement= LtkModelUtils.getSourceContainerElement(
+ (SourceStructElement<?, ?>)element);
+ if (containerElement != null) {
+ final var sourceUnit= element.getSourceUnit();
+ return (containerElement.getStamp().getContentStamp() == sourceUnit.getContentStamp(null));
+ }
+ }
+ return true;
+ }
+
+ private boolean hasAstError(final SourceElement<?> sourceElement) {
+ final var astNode= sourceElement.getAdapter(AstNode.class);
+ return (astNode != null && Asts.hasErrors(astNode));
+ }
+
+ private int getProblemFlag(final IAnnotationModel model, final SourceElement<?> sourceElement) {
+ int severity= -1;
+ for (final var iter= model.getAnnotationIterator();
+ (severity != Problem.SEVERITY_ERROR && iter.hasNext()); ) {
+ final var annotation= iter.next();
+ if (annotation instanceof SourceIssueEditorAnnotation) {
+ final var issueAnnotation= (SourceIssueEditorAnnotation)annotation;
+ if (!issueAnnotation.isMarkedDeleted()) {
+ final Issue issue= issueAnnotation.getIssue();
+ if (issue instanceof Problem) {
+ if (isInside(model.getPosition(annotation), sourceElement)) {
+ severity= Math.max(severity, ((Problem)issue).getSeverity());
+ }
+ }
+ }
+ continue;
+ }
+ }
+ switch (severity) {
+ case Problem.SEVERITY_INFO:
+ return DecoratedElementImageDescriptor.INFO;
+ case Problem.SEVERITY_WARNING:
+ return DecoratedElementImageDescriptor.WARNING;
+ case Problem.SEVERITY_ERROR:
+ return DecoratedElementImageDescriptor.ERROR;
+ default:
+ return 0;
+ }
+ }
+
+ private boolean isInside(final @Nullable Position position,
+ final SourceElement<?> sourceElement) {
+ return (position != null
+ && (isInside(position, sourceElement.getSourceRange())
+ || isInside(position, sourceElement.getDocumentationRange()) ));
+ }
+
+ private boolean isInside(final Position position, final @Nullable TextRegion region) {
+ return (region != null && region.contains(position.getOffset()));
+ }
+
+
@Override
public void update(final ViewerCell cell) {
final Object cellElement= cell.getElement();