/*=============================================================================#
# Copyright (c) 2010-2016 Stephan Wahlbrink (WalWare.de) 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:
# Stephan Wahlbrink - initial API and implementation
#=============================================================================*/
package de.walware.ecommons.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 de.walware.ecommons.ICommonStatusConstants;
import de.walware.ecommons.IDisposable;
import de.walware.ecommons.preferences.PreferencesUtil;
import de.walware.ecommons.preferences.SettingsChangeNotifier;
import de.walware.ecommons.preferences.SettingsChangeNotifier.ManageListener;
import de.walware.ecommons.preferences.core.IPreferenceAccess;
import de.walware.ecommons.preferences.core.Preference;
import de.walware.ecommons.preferences.core.Preference.StringArrayPref;
import de.walware.ecommons.ui.SharedUIResources;
import de.walware.ecommons.ltk.internal.ui.AdvancedExtensionsInternal;
/**
* Registry for information hover types of a content type.
*/
public class InfoHoverRegistry implements ManageListener, IDisposable {
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(PreferencesUtil.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.PLUGIN_ID, ICommonStatusConstants.INTERNAL_PLUGGED_IN,
NLS.bind("Loading Text Hover failed (id= ''{0}'', contributed by= ''{1}'')", //$NON-NLS-1$
(id != null) ? id : "", element.getDeclaringExtension().getContributor().getName()), e)); //$NON-NLS-1$
}
}
return applyPreferences(PreferencesUtil.getInstancePrefs(), descriptors);
}
List<InfoHoverDescriptor> applyPreferences(final IPreferenceAccess 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);
}
}