/**
* Copyright (c) 2013 Puppet Labs, Inc. and other contributors, as listed below.
* 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:
* Puppet Labs
*/
package com.puppetlabs.geppetto.pp.dsl.ui.editor.hover;
import java.io.IOException;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import com.puppetlabs.geppetto.pp.AssignmentExpression;
import com.puppetlabs.geppetto.pp.AttributeOperation;
import com.puppetlabs.geppetto.pp.Definition;
import com.puppetlabs.geppetto.pp.ExpressionTE;
import com.puppetlabs.geppetto.pp.LiteralNameOrReference;
import com.puppetlabs.geppetto.pp.NodeDefinition;
import com.puppetlabs.geppetto.pp.PPPackage;
import com.puppetlabs.geppetto.pp.VariableExpression;
import com.puppetlabs.geppetto.pp.VariableTE;
import com.puppetlabs.geppetto.pp.VerbatimTE;
import com.puppetlabs.geppetto.pp.dsl.adapters.CrossReferenceAdapter;
import com.puppetlabs.geppetto.pp.dsl.ui.internal.PPDSLActivator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.URIConverter;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.internal.text.html.BrowserInformationControl;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.text.AbstractReusableInformationControlCreator;
import org.eclipse.jface.text.DefaultInformationControl;
import org.eclipse.jface.text.IInformationControl;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.IInputChangedListener;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.ui.editor.hover.html.DefaultEObjectHoverProvider;
import org.eclipse.xtext.ui.editor.hover.html.IXtextBrowserInformationControl;
import org.eclipse.xtext.ui.editor.hover.html.XtextBrowserInformationControl;
import org.eclipse.xtext.ui.editor.hover.html.XtextBrowserInformationControlInput;
import org.eclipse.xtext.util.Files;
import org.eclipse.xtext.util.PolymorphicDispatcher;
import com.google.inject.Inject;
/**
* Provides Hover information - for what should there be a hover, and what (the actual content is
* produce by {@link PPDocumentationProvider} (labels, documentation, and images).
*
*/
public class PPHoverProvider extends DefaultEObjectHoverProvider {
/**
* Basically a copy of the class with the same name in DefaultEObjectHoverProvider.
* A complete copy required since it is not possible to access the openDeclarationAction
* instance and siable it from the outside.
*
*/
public class PresenterControlCreator extends AbstractReusableInformationControlCreator {
/**
* @since 2.3
*/
protected void configureControl(final IXtextBrowserInformationControl control, ToolBarManager tbm, String font) {
final BackAction backAction = new BackAction(control);
backAction.setEnabled(false);
tbm.add(backAction);
final ForwardAction forwardAction = new ForwardAction(control);
tbm.add(forwardAction);
forwardAction.setEnabled(false);
final OpenDeclarationAction openDeclarationAction = new OpenDeclarationAction(control);
tbm.add(openDeclarationAction);
IInputChangedListener inputChangeListener = new IInputChangedListener() {
public void inputChanged(Object newInput) {
backAction.update();
forwardAction.update();
if(newInput == null) {
}
else if(newInput instanceof XtextBrowserInformationControlInput) {
URI uri = createURI(((XtextBrowserInformationControlInput) newInput).getElement());
openDeclarationAction.setEnabled(uri != null);
}
}
};
control.addInputChangeListener(inputChangeListener);
tbm.update(true);
addLinkListener(control);
}
@Override
public IInformationControl doCreateInformationControl(Shell parent) {
if(BrowserInformationControl.isAvailable(parent)) {
ToolBarManager tbm = new ToolBarManager(SWT.FLAT);
String font = "org.eclipse.jdt.ui.javadocfont"; // FIXME: mPreferenceConstants.APPEARANCE_JAVADOC_FONT;
IXtextBrowserInformationControl control = new XtextBrowserInformationControl(parent, font, tbm);
configureControl(control, tbm, font);
return control;
}
// else {
return new DefaultInformationControl(parent, true);
// }
}
}
private PolymorphicDispatcher<Boolean> hoverDispatcher = new PolymorphicDispatcher<Boolean>(
"_hover", 1, 1, Collections.singletonList(this), PolymorphicDispatcher.NullErrorHandler.<Boolean> get()) {
@Override
protected Boolean handleNoSuchMethod(Object... params) {
return false;
}
};
private static final String styleSheetFileName = "/css/PPHoverStyleSheet.css";
@Inject
private PPDocumentationProvider documentationProvider;
private PresenterControlCreator presenterControlCreator;
protected Boolean _hover(AttributeOperation o) {
return true;
}
protected Boolean _hover(Definition o) {
return true;
}
protected Boolean _hover(EObject o) {
List<IEObjectDescription> xrefs = CrossReferenceAdapter.get(o);
if(xrefs != null && xrefs.size() > 0)
return true;
return false;
}
protected Boolean _hover(LiteralNameOrReference o) {
EStructuralFeature feature = o.eContainingFeature();
if(feature == PPPackage.Literals.RESOURCE_EXPRESSION__RESOURCE_EXPR)
return true;
if(feature == PPPackage.Literals.PARENTHESISED_EXPRESSION__EXPR) {
if(o.eContainer().eContainer() instanceof ExpressionTE)
return true;
}
List<IEObjectDescription> xrefs = CrossReferenceAdapter.get(o);
if(xrefs != null && xrefs.size() > 0)
return true;
return false;
}
protected Boolean _hover(NodeDefinition o) {
return true;
}
protected Boolean _hover(VariableExpression o) {
if(o.eContainer() instanceof AssignmentExpression &&
o.eContainingFeature() == PPPackage.Literals.BINARY_EXPRESSION__LEFT_EXPR)
return false;
return true;
}
protected Boolean _hover(VariableTE o) {
return true;
}
protected Boolean _hover(VerbatimTE o) {
return hasHover(o.eContainer());
}
/**
* Overrides the default implementation which does not seem to link to something somewhere else than in
* the same file. Also, does not handle hovers without a target very well.
*
* @see org.eclipse.xtext.ui.editor.hover.html.DefaultEObjectHoverProvider#createURI(org.eclipse.emf.ecore.EObject)
*/
@Override
protected org.eclipse.emf.common.util.URI createURI(EObject o) {
IEObjectDescription target = documentationProvider.xref(o);
if(target == null)
return null; // should disable the link as well
final URIConverter uriConverter = o.eResource().getResourceSet().getURIConverter();
final URI uri = target.getEObjectURI();
final URI normalized = uriConverter.normalize(uri);
return normalized;
}
@Override
protected String getFirstLine(EObject o) {
StringBuilder builder = new StringBuilder();
Image image = documentationProvider.getImage(o);
if(image != null) {
URL imageURL = PPDSLActivator.getDefault().getImagesOnFSRegistry().getImageURL(
ImageDescriptor.createFromImage(image));
builder.append("<IMG src=\"").append(imageURL.toExternalForm()).append("\"/>");
}
builder.append("<b>").append(getLabel(o)).append("</b>");
String label = documentationProvider.getText(o);
if(label != null)
builder.append(" <span class='target'>- ").append(documentationProvider.getText(o)).append("</span>");
return builder.toString();
}
/**
* Overrides the default implementation simply because the PPPresenterControlCreator always enables
* the OpenDeclaration link.
*
* @see org.eclipse.xtext.ui.editor.hover.html.DefaultEObjectHoverProvider#getInformationPresenterControlCreator()
*/
@Override
public IInformationControlCreator getInformationPresenterControlCreator() {
if(presenterControlCreator == null)
presenterControlCreator = new PresenterControlCreator(); // specialisation
return presenterControlCreator;
}
@Override
protected boolean hasHover(EObject o) {
Boolean result = hoverDispatcher.invoke(o);
// TODO: make this a trace setting instead...
// System.out.println("Hover query (" + result + ") for: " + o.getClass()); // For debugging, finding what to react to
return result;
}
@Override
protected String loadStyleSheet() {
URL styleSheetURL = PPDSLActivator.getInstance().getBundle().getEntry(styleSheetFileName);
if(styleSheetURL != null)
try {
return Files.readStreamIntoString(styleSheetURL.openStream());
}
catch(IOException e) {
// ignore
}
return super.loadStyleSheet();
}
}