blob: 2272c876860bffe82ca0440f5ade72e61fc895bb [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2010, 2020 Stephan Wahlbrink 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, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.ltk.ui.sourceediting.assist;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.osgi.util.NLS;
import org.eclipse.ui.statushandlers.StatusManager;
import org.eclipse.statet.jcommons.lang.Disposable;
import org.eclipse.statet.ecommons.preferences.PreferencesUtil;
import org.eclipse.statet.ecommons.preferences.SettingsChangeNotifier;
import org.eclipse.statet.ecommons.preferences.SettingsChangeNotifier.ManageListener;
import org.eclipse.statet.ecommons.preferences.core.Preference;
import org.eclipse.statet.ecommons.preferences.core.Preference.StringArrayPref;
import org.eclipse.statet.ecommons.preferences.core.PreferenceAccess;
import org.eclipse.statet.ecommons.preferences.core.util.PreferenceUtils;
import org.eclipse.statet.ecommons.ui.SharedUIResources;
import org.eclipse.statet.internal.ltk.ui.AdvancedExtensionsInternal;
/**
* Registry for information hover types of a content type.
*/
public class InfoHoverRegistry implements ManageListener, Disposable {
public static final String TYPE_SETTINGS= "hover.type_settings.ids:setting"; //$NON-NLS-1$
public static final class EffectiveHovers {
private final int[] stateMasks;
private final List<InfoHoverDescriptor> stateMaskDescriptors;
private final List<InfoHoverDescriptor> combinedDescriptors;
public EffectiveHovers(final int[] stateMasks, final List<InfoHoverDescriptor> effectiveDescriptors,
final List<InfoHoverDescriptor> combinedDescriptors) {
this.stateMasks= stateMasks;
this.stateMaskDescriptors= effectiveDescriptors;
this.combinedDescriptors= combinedDescriptors;
}
public int[] getStateMasks() {
return this.stateMasks;
}
public InfoHoverDescriptor getDescriptor(final int stateMask) {
for (final InfoHoverDescriptor descriptor : this.stateMaskDescriptors) {
if (descriptor.getStateMask() == stateMask) {
return descriptor;
}
}
return null;
}
public List<InfoHoverDescriptor> getDescriptorsForCombined() {
return this.combinedDescriptors;
}
}
private final String contentTypeId;
private final String settingsGroupId;
private final StringArrayPref prefTypeSettings;
private List<InfoHoverDescriptor> descriptors;
private EffectiveHovers effectiveHovers;
public InfoHoverRegistry(final String contentTypeId, final String prefQualifier,
final String settingsGroupId) {
this.contentTypeId= contentTypeId;
this.settingsGroupId= settingsGroupId;
this.prefTypeSettings= new StringArrayPref(prefQualifier, TYPE_SETTINGS);
PreferencesUtil.getSettingsChangeNotifier().addManageListener(this);
}
@Override
public void dispose() {
final SettingsChangeNotifier notifier= PreferencesUtil.getSettingsChangeNotifier();
if (notifier != null) {
notifier.removeManageListener(this);
}
}
@Override
public void beforeSettingsChangeNotification(final Set<String> groupIds) {
if (this.settingsGroupId != null && groupIds.contains(this.settingsGroupId)) {
synchronized (this) {
this.descriptors= applyPreferences(PreferenceUtils.getInstancePrefs(),
new ArrayList<>(this.descriptors));
this.effectiveHovers= null;
}
}
}
@Override
public void afterSettingsChangeNotification(final Set<String> groupIds) {
}
String getSettingsGroupId() {
return this.settingsGroupId;
}
StringArrayPref getPrefSeparateSettings() {
return this.prefTypeSettings;
}
List<InfoHoverDescriptor> loadCurrent() {
final IExtensionRegistry extensionRegistry= Platform.getExtensionRegistry();
final IConfigurationElement[] elements= extensionRegistry.getConfigurationElementsFor(
AdvancedExtensionsInternal.INFOHOVER_EXTENSIONPOINT_ID);
final List<InfoHoverDescriptor> descriptors= new ArrayList<>();
for (final IConfigurationElement element : elements) {
String id= null;
try {
final String contentTypeId= AdvancedExtensionsInternal.getCheckedString(element,
AdvancedExtensionsInternal.CONFIG_CONTENT_TYPE_ID_ATTRIBUTE_NAME);
if (!this.contentTypeId.equals(contentTypeId)) {
continue;
}
id= AdvancedExtensionsInternal.getCheckedString(element,
AdvancedExtensionsInternal.CONFIG_ID_ATTRIBUTE_NAME).intern();
final String name= AdvancedExtensionsInternal.getCheckedString(element,
AdvancedExtensionsInternal.CONFIG_NAME_ATTRIBUTE_NAME);
final InfoHoverDescriptor descriptor= new InfoHoverDescriptor(id,
name, element);
descriptors.add(descriptor);
}
catch (final CoreException e) {
StatusManager.getManager().handle(new Status(IStatus.ERROR, SharedUIResources.BUNDLE_ID, 0,
NLS.bind("Loading Text Hover failed (id= ''{0}'', contributed by= ''{1}'')", //$NON-NLS-1$
(id != null) ? id : "", element.getDeclaringExtension().getContributor().getName()), //$NON-NLS-1$
e ));
}
}
return applyPreferences(PreferenceUtils.getInstancePrefs(), descriptors);
}
List<InfoHoverDescriptor> applyPreferences(final PreferenceAccess prefAccess, final List<InfoHoverDescriptor> descriptors) {
final String[] settings= prefAccess.getPreferenceValue(getPrefSeparateSettings());
final List<InfoHoverDescriptor> sortedDescriptors= new ArrayList<>(descriptors.size());
for (final String setting : settings) {
final int idx1= setting.indexOf(':');
if (idx1 >= 0) {
final int idx2= setting.indexOf(';', idx1+1);
if (idx2 >= 0) {
final String id= setting.substring(0, idx1);
for (final InfoHoverDescriptor descriptor : descriptors) {
if (descriptor.getId().equals(id)) {
descriptors.remove(descriptor);
descriptor.isEnabled= Boolean.parseBoolean(setting.substring(idx1+1, idx2));
descriptor.stateMask= AdvancedExtensionsInternal.computeStateMask(setting.substring(idx2+1));
sortedDescriptors.add(descriptor);
break;
}
}
}
}
}
for (final InfoHoverDescriptor descriptor : descriptors) {
descriptor.isEnabled= false;
descriptor.stateMask= 0;
sortedDescriptors.add(descriptor);
}
return sortedDescriptors;
}
Map<Preference<?>, Object> toPreferencesMap(final List<InfoHoverDescriptor> descriptors) {
final Map<Preference<?>, Object> map= new HashMap<>();
final String[] settings= new String[descriptors.size()];
for (int i= 0; i < settings.length; i++) {
final InfoHoverDescriptor descriptor= descriptors.get(i);
settings[i]= descriptor.getId() + ':' + descriptor.isEnabled() + ';' +
AdvancedExtensionsInternal.createUnifiedStateMaskString(descriptor.getStateMask());
}
map.put(this.prefTypeSettings, settings);
return map;
}
public String getContentTypeId() {
return this.contentTypeId;
}
public synchronized InfoHoverDescriptor getHoverDescriptor(final int stateMask) {
List<InfoHoverDescriptor> descriptors= this.descriptors;
if (descriptors == null) {
this.descriptors= descriptors= loadCurrent();
}
for (final InfoHoverDescriptor descriptor : descriptors) {
if (descriptor.isEnabled() && descriptor.getStateMask() == stateMask) {
return descriptor;
}
}
return null;
}
public synchronized EffectiveHovers getEffectiveHoverDescriptors() {
if (this.effectiveHovers == null) {
updateEffectiveHovers();
}
return this.effectiveHovers;
}
private void updateEffectiveHovers() {
List<InfoHoverDescriptor> descriptors= this.descriptors;
if (descriptors == null) {
this.descriptors= descriptors= loadCurrent();
}
int[] stateMasks= new int[descriptors.size()];
final List<InfoHoverDescriptor> effectiveDescriptors= new ArrayList<>(descriptors.size());
final List<InfoHoverDescriptor> combinedDescriptors= new ArrayList<>(descriptors.size());
for (final InfoHoverDescriptor descriptor : descriptors) {
if (!descriptor.getId().endsWith("CombinedHover")) { //$NON-NLS-1$
combinedDescriptors.add(descriptor);
}
if (descriptor.isEnabled()) {
final int stateMask= descriptor.getStateMask();
if (stateMask == -1) {
continue;
}
for (int i= 0; i < effectiveDescriptors.size(); i++) {
if (stateMasks[i] == stateMask) {
continue;
}
}
stateMasks[effectiveDescriptors.size()]= stateMask;
effectiveDescriptors.add(descriptor);
}
}
if (stateMasks.length != effectiveDescriptors.size()) {
final int[] fittedMasks= new int[effectiveDescriptors.size()];
System.arraycopy(stateMasks, 0, fittedMasks, 0, effectiveDescriptors.size());
stateMasks= fittedMasks;
}
this.effectiveHovers= new EffectiveHovers(stateMasks, effectiveDescriptors, combinedDescriptors);
}
}