blob: bf5a0e872ec333caac3c33fa7666fa66ca8531fa [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2017 Willink Transformations 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:
* E.D.Willink - initial API and implementation
*******************************************************************************/
package org.eclipse.ocl.xtext.markup.ui.hover;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.ocl.pivot.CallExp;
import org.eclipse.ocl.pivot.Element;
import org.eclipse.ocl.pivot.Namespace;
import org.eclipse.ocl.pivot.OCLExpression;
import org.eclipse.ocl.pivot.ReferringElement;
import org.eclipse.ocl.pivot.TemplateableElement;
import org.eclipse.ocl.pivot.Type;
import org.eclipse.ocl.pivot.internal.prettyprint.PrettyPrintOptions;
import org.eclipse.ocl.pivot.internal.prettyprint.PrettyPrinter;
import org.eclipse.ocl.pivot.internal.utilities.EnvironmentFactoryInternal;
import org.eclipse.ocl.pivot.internal.utilities.HTMLBuffer;
import org.eclipse.ocl.pivot.internal.utilities.PivotUtilInternal;
import org.eclipse.ocl.pivot.utilities.EnvironmentFactory;
import org.eclipse.ocl.pivot.utilities.PivotUtil;
import org.eclipse.ocl.pivot.utilities.Pivotable;
import org.eclipse.ocl.xtext.basecs.ElementCS;
import org.eclipse.ocl.xtext.markup.MarkupUtils;
import org.eclipse.ocl.xtext.markupcs.Markup;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.parser.IParseResult;
import org.eclipse.xtext.ui.editor.hover.html.DefaultEObjectHoverProvider;
import org.eclipse.xtext.ui.editor.hover.html.XtextBrowserInformationControlInput;
public class MarkupHoverProvider extends DefaultEObjectHoverProvider
{
@Override
protected String getDocumentation(EObject o) {
Resource resource = o.eResource();
if (resource == null) {
return null;
}
if (o instanceof Pivotable) {
EObject o1 = ((Pivotable)o).getPivot();
if (o1 != null) {
o = o1;
}
if (o instanceof ReferringElement) {
EObject o2 = ((ReferringElement)o).getReferredElement();
if (o2 != null) {
o = o2;
}
}
if (o instanceof TemplateableElement) {
EObject o3 = ((TemplateableElement)o).getUnspecializedElement();
if (o3 != null) {
o = o3;
}
}
}
String documentation = super.getDocumentation(o);
if (documentation == null) {
return null;
}
IParseResult parseResult = MarkupUtils.decode(documentation);
if (parseResult == null) {
return null;
}
Markup markup = (Markup) parseResult.getRootASTElement();
if (markup == null) {
return null;
}
Iterable<INode> parseErrors = parseResult.getSyntaxErrors();
Map<Integer, @NonNull Integer> errorMap = getErrorMap(parseErrors);
if (errorMap != null) {
HTMLBuffer htmlBuffer = new HTMLBuffer();
int i = 0;
List<Integer> starts = new ArrayList<Integer>(errorMap.keySet());
Collections.sort(starts);
for (int start : starts) {
Integer end = errorMap.get(start);
assert end != null;
while (i < start) {
htmlBuffer.append(documentation.charAt(i++));
}
htmlBuffer.startFontColor("red");
htmlBuffer.startUnderline();
while (i < end) {
htmlBuffer.append(documentation.charAt(i++));
}
htmlBuffer.endUnderline();
htmlBuffer.endFontColor();
}
htmlBuffer.startFontColor("red");
htmlBuffer.startParagraph();
htmlBuffer.endParagraph();
for (INode parseError :parseErrors) {
htmlBuffer.startParagraph();
htmlBuffer.append(parseError.getSyntaxErrorMessage().getMessage());
htmlBuffer.endParagraph();
}
htmlBuffer.endFontColor();
return htmlBuffer.toString();
}
if (o instanceof Pivotable) {
o = ((Pivotable)o).getPivot();
}
if (o == null) {
return null;
}
try {
EnvironmentFactoryInternal environmentFactory = PivotUtilInternal.findEnvironmentFactory(resource);
if (environmentFactory == null) {
return null;
}
return MarkupUtils.toHTML(environmentFactory, o, markup);
} catch (Exception e) {
StringWriter s = new StringWriter();
e.printStackTrace(new PrintWriter(s));
return s.toString().replace("\n", "\n<br>");
}
}
protected Map<Integer, @NonNull Integer> getErrorMap(Iterable<INode> parseErrors) {
Map<Integer, @NonNull Integer> errorMap = null;
for (INode parseError : parseErrors) {
if (errorMap == null) {
errorMap = new HashMap<Integer, @NonNull Integer>();
}
int start = parseError.getOffset();
errorMap.put(start, start + parseError.getLength());
}
if (errorMap != null) {
List<Integer> starts = new ArrayList<Integer>(errorMap.keySet());
Collections.sort(starts);
Integer currentStart = null;
Integer currentEnd = 0;
for (int i = 0; i < starts.size(); i++) {
Integer start = starts.get(i);
if (currentStart == null) {
currentStart = start;
currentEnd = errorMap.get(start);
}
else {
currentEnd = errorMap.get(start);
assert currentEnd != null;
if (start > currentEnd) {
currentStart = start;
}
else {
errorMap.remove(start);
errorMap.put(currentStart, currentEnd);
}
}
}
}
return errorMap;
}
@Override
protected String getFirstLine(EObject eObject) {
// System.out.println("getFirstLine " + eObject.eClass().getName());
Element pivotElement = null;
if (eObject instanceof Pivotable) {
pivotElement = PivotUtil.getPivot(Element.class, (Pivotable)eObject);
}
else if (eObject instanceof Element) {
pivotElement = (Element)eObject;
}
if (pivotElement != null) {
Namespace namespace = getNamespace(pivotElement);
if ((namespace != null) && (namespace.eContainer() instanceof Namespace)) {
namespace = (Namespace) namespace.eContainer();
}
String description = null;
PrettyPrintOptions.Global prettyPrintOptions = new PrettyPrintOptions.Global(namespace)
{
{ setShowDefaultMultiplicities(true); }
@Override
public @Nullable Set<String> getReservedNames() {
return null;
}
@Override
public @Nullable Set<String> getRestrictedNames() {
return null;
}
};
if (namespace != null) {
EnvironmentFactory environmentFactory = PivotUtilInternal.findEnvironmentFactory(namespace);
if (environmentFactory != null) {
prettyPrintOptions.setEnvironmentFactory(environmentFactory);
}
}
if (pivotElement instanceof CallExp) {
description = PrettyPrinter.printType(pivotElement, prettyPrintOptions);
}
else if (pivotElement instanceof ReferringElement) {
Element referredElement = ((ReferringElement)pivotElement).getReferredElement();
if (referredElement != null) {
description = PrettyPrinter.print(referredElement, prettyPrintOptions);
}
}
else if (pivotElement instanceof OCLExpression) {
Type type = ((OCLExpression)pivotElement).getType();
if (type != null) {
description = PrettyPrinter.printType(type, prettyPrintOptions);
}
}
if (description == null) {
description = PrettyPrinter.print(pivotElement, prettyPrintOptions);
}
// System.out.println(" => " + description);
return pivotElement.eClass().getName() + " <b>" + description + "</b>";
}
else {
String firstLine = super.getFirstLine(eObject);
// System.out.println(" => " + firstLine);
return firstLine + "\n<br>" + eObject.eClass().getName(); // FIXME do better
}
}
public static Namespace getNamespace(EObject element) {
int count = 0;
for (EObject eObject = element; eObject != null; eObject = eObject.eContainer()) {
if (eObject instanceof Namespace) {
if (count++ >= 2) {
return (Namespace) eObject;
}
}
if (eObject instanceof Type) {
count++;
}
}
return null;
}
@Override
protected boolean hasHover(EObject o) {
if (o instanceof Element) {
return true;
}
if (o instanceof ElementCS) {
return true;
}
return super.hasHover(o);
}
@Override
public IInformationControlCreator getHoverControlCreator() {
// TODO Auto-generated method stub
return super.getHoverControlCreator();
}
@Override
public IInformationControlCreatorProvider getHoverInfo(final EObject object, final ITextViewer viewer, final IRegion region) {
return new IInformationControlCreatorProvider() {
@Override
public IInformationControlCreator getHoverControlCreator() {
return MarkupHoverProvider.this.getHoverControlCreator();
}
@Override
public Object getInfo() {
// return getHoverInfo(object, region, null);
return getHoverInfo(object, viewer, region, null);
}
};
}
protected XtextBrowserInformationControlInput getHoverInfo(EObject element,
ITextViewer viewer, IRegion hoverRegion, XtextBrowserInformationControlInput previous) {
// IXtextDocument xtextDocument = XtextDocumentUtil.get(viewer);
return super.getHoverInfo(element, hoverRegion, previous);
}
}