/*******************************************************************************
* Copyright (c) 2000, 2011 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Mark Leone - Modifications and enhancements for PyDev
*******************************************************************************/
package org.python.pydev.editor.hover;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.Platform;
import org.eclipse.swt.SWT;
import org.osgi.framework.Bundle;
import org.python.pydev.core.ExtensionHelper;
import org.python.pydev.core.log.Log;
import org.python.pydev.editor.actions.PyAction;
import org.python.pydev.plugin.PydevPlugin;
import org.python.pydev.plugin.preferences.PydevPrefs;
/**
* Describes a PyDev editor text hover.
*
* @since 2.1
*/
public class PyEditorTextHoverDescriptor {
private static final String HOVER_TAG = "pyTextHover"; //$NON-NLS-1$
private static final String LABEL_ATTRIBUTE = "label"; //$NON-NLS-1$
private static final String ACTIVATE_PLUG_IN_ATTRIBUTE = "activate"; //$NON-NLS-1$
private static final String DESCRIPTION_ATTRIBUTE = "description"; //$NON-NLS-1$
public static final String ATT_PYDEV_HOVER_PRIORITY = "priority";
public static final String ATT_PYDEV_HOVER_LABEL = "label";
public static final String ATT_PYDEV_HOVER_CLASS = "class";
public static final String ATT_PYDEV_HOVER_ID = "id";
public static final String ATT_PYDEV_HOVER_PREEMPT = "preempt";
public static final String ATT_PYDEV_HOVER_ENABLE = "enable";
public static final int DEFAULT_HOVER_PRIORITY = 100;
public static final String NO_MODIFIER = "0"; //$NON-NLS-1$
public static final Integer DEFAULT_MODIFIER_MASK = 0;
public static final int HIGHEST_PRIORITY = 1;
private static final String COMBINING_HOVER_ID = "org.python.pydev.combininghover";
private static final String COMBINING_HOVER_LABEL = "PyDev Combining Hover";
private static final String COMBINING_HOVER_DESCR = "A Text Hover which combines hover info from contributed hovers";
int fStateMask;
String fModifierString;
private boolean fIsEnabled;
private IConfigurationElement fElement;
private Integer fPriority;
private Boolean fPreempt;
private AbstractPyEditorTextHover fHover;
/**
* Returns all PyDev editor text hovers contributed to the workbench.
*
* @return an array with the contributed text hovers
*/
public static PyEditorTextHoverDescriptor[] getContributedHovers() {
IExtensionRegistry registry = Platform.getExtensionRegistry();
IConfigurationElement[] elements = registry
.getConfigurationElementsFor(ExtensionHelper.PYDEV_HOVER2);
PyEditorTextHoverDescriptor[] hoverDescs = createDescriptors(elements);
initializeDefaultHoverPreferences(elements);
initializeHoversFromPreferences(hoverDescs);
return hoverDescs;
}
/**
* Computes the state mask for the given modifier string.
*
* @param modifiers the string with the modifiers, separated by '+', '-', ';', ',' or '.'
* @return the state mask or -1 if the input is invalid
*/
public static int computeStateMask(String modifiers) {
if (modifiers == null) {
return -1;
}
if (modifiers.length() == 0) {
return SWT.NONE;
}
int stateMask = 0;
StringTokenizer modifierTokenizer = new StringTokenizer(modifiers, ",;.:+-* "); //$NON-NLS-1$
while (modifierTokenizer.hasMoreTokens()) {
int modifier = PyAction.findLocalizedModifier(modifierTokenizer.nextToken());
if (modifier == 0 || (stateMask & modifier) == modifier) {
return -1;
}
stateMask = stateMask | modifier;
}
return stateMask;
}
/**
* Creates a new PyDev Editor text hover descriptor from the given combining
* hover implementation.
*
* @param element the combining hover
*/
public PyEditorTextHoverDescriptor(PydevCombiningHover hover) {
Assert.isNotNull(hover);
fHover = hover;
fPriority = HIGHEST_PRIORITY;
fIsEnabled = PyHoverPreferencesPage.getCombineHoverInfo();
}
/**
* Creates a new PyDev Editor text hover descriptor from the given configuration element.
*
* @param element the configuration element
*/
private PyEditorTextHoverDescriptor(IConfigurationElement element) {
Assert.isNotNull(element);
fElement = element;
}
/**
* Creates the PyDev editor text hover.
*
* @return the text hover
*/
public AbstractPyEditorTextHover createTextHover() {
if (fElement != null) {
String pluginId = fElement.getContributor().getName();
boolean isHoversPlugInActivated = Platform.getBundle(pluginId).getState() == Bundle.ACTIVE;
if (isHoversPlugInActivated || canActivatePlugIn()) {
try {
return (AbstractPyEditorTextHover) fElement
.createExecutableExtension(ATT_PYDEV_HOVER_CLASS);
} catch (CoreException x) {
Log.log(x);
}
}
return null;
} else {
return fHover;
}
}
//---- XML Attribute accessors ---------------------------------------------
/**
* Returns the hover's id.
*
* @return the id
*/
public String getId() {
if (fElement != null) {
return fElement.getAttribute(ATT_PYDEV_HOVER_ID);
}
return COMBINING_HOVER_ID;
}
/**
* Returns the hover's class name.
*
* @return the class name
*/
public String getHoverClassName() {
if (fElement != null) {
return fElement.getAttribute(ATT_PYDEV_HOVER_CLASS);
}
return fHover.getClass().getName();
}
/**
* Returns the hover's label.
*
* @return the label
*/
public String getLabel() {
if (fElement != null) {
String label = fElement.getAttribute(LABEL_ATTRIBUTE);
if (label != null) {
return label;
}
// Return simple class name
label = getHoverClassName();
int lastDot = label.lastIndexOf('.');
if (lastDot >= 0 && lastDot < label.length() - 1) {
return label.substring(lastDot + 1);
} else {
return label;
}
}
return COMBINING_HOVER_LABEL;
}
/**
* Returns the hover's description.
*
* @return the hover's description or <code>null</code> if not provided
*/
public String getDescription() {
if (fElement != null) {
return fElement.getAttribute(DESCRIPTION_ATTRIBUTE);
}
return COMBINING_HOVER_DESCR;
}
public boolean canActivatePlugIn() {
return (fElement == null ? false
: Boolean.valueOf(fElement.getAttribute(ACTIVATE_PLUG_IN_ATTRIBUTE)).booleanValue());
}
@Override
public boolean equals(Object obj) {
if (obj == null || !obj.getClass().equals(this.getClass()) || getId() == null) {
return false;
}
return getId().equals(((PyEditorTextHoverDescriptor) obj).getId());
}
@Override
public int hashCode() {
return getId().hashCode();
}
public static PyEditorTextHoverDescriptor[] createDescriptors(IConfigurationElement[] elements) {
List<PyEditorTextHoverDescriptor> result = new ArrayList<PyEditorTextHoverDescriptor>(elements.length);
for (int i = 0; i < elements.length; i++) {
IConfigurationElement element = elements[i];
if (HOVER_TAG.equals(element.getName())) {
PyEditorTextHoverDescriptor desc = new PyEditorTextHoverDescriptor(element);
result.add(desc);
}
}
return result.toArray(new PyEditorTextHoverDescriptor[result.size()]);
}
public static void initializeDefaultHoverPreferences(IConfigurationElement[] elements) {
for (IConfigurationElement element : elements) {
String id = element.getAttribute(PyEditorTextHoverDescriptor.ATT_PYDEV_HOVER_ID);
//modifier
PydevPrefs.getPreferenceStore().setDefault(
PyHoverPreferencesPage.KEY_TEXT_HOVER_MODIFIER + id, PyEditorTextHoverDescriptor.NO_MODIFIER);
//state mask
PydevPrefs.getPreferenceStore().setDefault(
PyHoverPreferencesPage.KEY_TEXT_HOVER_MODIFIER_MASK + id,
PyEditorTextHoverDescriptor.DEFAULT_MODIFIER_MASK);
//preempt
String attVal = element.getAttribute(PyEditorTextHoverDescriptor.ATT_PYDEV_HOVER_PREEMPT);
PydevPlugin.getDefault().getPreferenceStore().setDefault(
PyHoverPreferencesPage.KEY_TEXT_HOVER_PREEMPT + id, Boolean.parseBoolean(attVal));
//enable
attVal = element.getAttribute(PyEditorTextHoverDescriptor.ATT_PYDEV_HOVER_ENABLE);
PydevPlugin.getDefault().getPreferenceStore().setDefault(
PyHoverPreferencesPage.KEY_TEXT_HOVER_ENABLE + id, Boolean.parseBoolean(attVal));
//priority
attVal = element.getAttribute(PyEditorTextHoverDescriptor.ATT_PYDEV_HOVER_PRIORITY);
int priority;
try {
priority = Integer.parseInt(attVal);
priority = (priority >= HIGHEST_PRIORITY ? priority : HIGHEST_PRIORITY);
} catch (NumberFormatException e) {
priority = DEFAULT_HOVER_PRIORITY;
}
PydevPlugin.getDefault().getPreferenceStore().setDefault(
PyHoverPreferencesPage.KEY_TEXT_HOVER_PRIORITY + id, priority);
}
}
public static void initializeHoversFromPreferences(PyEditorTextHoverDescriptor[] hovers) {
for (int i = 0; i < hovers.length; i++) {
//enable
hovers[i].fIsEnabled = PydevPlugin.getDefault().getPreferenceStore()
.getBoolean(PyHoverPreferencesPage.KEY_TEXT_HOVER_ENABLE + hovers[i].getId());
//preempt
hovers[i].fPreempt = PydevPlugin.getDefault().getPreferenceStore()
.getBoolean(PyHoverPreferencesPage.KEY_TEXT_HOVER_PREEMPT + hovers[i].getId());
//priority
String sPriority = PydevPlugin.getDefault().getPreferenceStore()
.getString(PyHoverPreferencesPage.KEY_TEXT_HOVER_PRIORITY + hovers[i].getId());
try {
hovers[i].fPriority = Integer.parseInt(sPriority);
} catch (NumberFormatException e) {
hovers[i].fPriority = DEFAULT_HOVER_PRIORITY;
}
//modifier
String modifierString = PydevPrefs.getPreferenceStore()
.getString(PyHoverPreferencesPage.KEY_TEXT_HOVER_MODIFIER + hovers[i].getId());
if (modifierString == null || modifierString.equals(PyEditorTextHoverDescriptor.NO_MODIFIER)) {
modifierString = ""; //$NON-NLS-1$
}
hovers[i].fModifierString = modifierString;
//state mask
hovers[i].fStateMask = PyEditorTextHoverDescriptor.computeStateMask(modifierString);
if (hovers[i].fStateMask == -1) {
// Fallback: use stored modifier masks
try {
hovers[i].fStateMask = Integer.parseInt(PydevPrefs.getPreferenceStore().getString(
PyHoverPreferencesPage.KEY_TEXT_HOVER_MODIFIER_MASK + hovers[i].getId()));
} catch (NumberFormatException ex) {
hovers[i].fStateMask = -1;
}
// Fix modifier string
int stateMask = hovers[i].fStateMask;
if (stateMask == -1) {
hovers[i].fModifierString = ""; //$NON-NLS-1$
} else {
hovers[i].fModifierString = PyAction.getModifierString(stateMask);
}
}
}
}
/**
* Returns the configured modifier getStateMask for this hover.
*
* @return the hover modifier stateMask or -1 if no hover is configured
*/
public int getStateMask() {
return fStateMask;
}
/**
* Returns the modifier String as set in the preference store.
*
* @return the modifier string
*/
public String getModifierString() {
return fModifierString;
}
/**
* Returns whether this hover is enabled or not.
*
* @return <code>true</code> if enabled
*/
public boolean isEnabled() {
return fIsEnabled;
}
/**
* Sets the hover's enabled state
*/
public void setIsEnabled(boolean enabled) {
fIsEnabled = enabled;
}
/**
* Returns the hover's priority.
*
* @return the priority
*/
public Integer getPriority() {
return fPriority;
}
/**
* Sets the hover's priority
*/
public void setPriority(int priority) {
fPriority = priority;
}
/**
* Returns the hover's preempt setting.
*
* @return the preempt setting
*/
public Boolean isPreempt() {
return fPreempt;
}
/**
* Sets the hover's preempt attribute
*/
public void setIsPreempt(boolean preempt) {
fPreempt = preempt;
}
/**
* Returns this hover descriptors configuration element.
*
* @return the configuration element
*/
public IConfigurationElement getConfigurationElement() {
return fElement;
}
}