| /******************************************************************************* |
| * Copyright (c) 2007, 2021 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.pde.api.tools.ui.internal.preferences; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IWorkspaceRoot; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.preferences.DefaultScope; |
| import org.eclipse.core.runtime.preferences.IEclipsePreferences; |
| import org.eclipse.core.runtime.preferences.IScopeContext; |
| import org.eclipse.core.runtime.preferences.InstanceScope; |
| import org.eclipse.jface.dialogs.Dialog; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.jface.window.Window; |
| import org.eclipse.pde.api.tools.internal.problems.ApiProblemFactory; |
| import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin; |
| import org.eclipse.pde.api.tools.internal.provisional.IApiMarkerConstants; |
| import org.eclipse.pde.api.tools.internal.provisional.descriptors.IElementDescriptor; |
| import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblem; |
| import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblemTypes; |
| import org.eclipse.pde.api.tools.internal.util.Util; |
| import org.eclipse.pde.api.tools.ui.internal.ApiUIPlugin; |
| import org.eclipse.pde.api.tools.ui.internal.SWTFactory; |
| import org.eclipse.pde.internal.ui.preferences.ConfigurationBlock; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.SelectionAdapter; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.events.SelectionListener; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.widgets.Combo; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Group; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.ui.preferences.IWorkbenchPreferenceContainer; |
| import org.eclipse.ui.preferences.IWorkingCopyManager; |
| import org.eclipse.ui.preferences.WorkingCopyManager; |
| import org.osgi.service.prefs.BackingStoreException; |
| |
| /** |
| * This block is used to add the API Tools profile notification settings UI to a |
| * parent control |
| * |
| * @since 1.0.0 |
| */ |
| public class ApiBaselinesConfigurationBlock extends ConfigurationBlock { |
| /** |
| * Provides data information for created controls |
| */ |
| protected static class ControlData { |
| Key key; |
| private String[] values; |
| |
| /** |
| * Constructor |
| * |
| * @param key |
| * @param values |
| */ |
| public ControlData(Key key, String[] values) { |
| this.key = key; |
| this.values = values; |
| } |
| |
| public Key getKey() { |
| return key; |
| } |
| |
| public String getValue(boolean selection) { |
| int index = selection ? 0 : 1; |
| return values[index]; |
| } |
| |
| public String getValue(int index) { |
| return values[index]; |
| } |
| |
| public int getSelection(String value) { |
| if (value != null) { |
| for (int i = 0; i < values.length; i++) { |
| if (value.equals(values[i])) { |
| return i; |
| } |
| } |
| } |
| return values.length - 1; // assume the last option is the least |
| // severe |
| } |
| } |
| |
| /** |
| * Provides management for changed/stored values for a given preference key |
| */ |
| protected static class Key { |
| |
| private String qualifier; |
| private String key; |
| |
| /** |
| * Constructor |
| * |
| * @param qualifier |
| * @param key |
| */ |
| public Key(String qualifier, String key) { |
| this.qualifier = qualifier; |
| this.key = key; |
| } |
| |
| /** |
| * Returns the {@link IEclipsePreferences} node for the given context |
| * and {@link IWorkingCopyManager} |
| * |
| * @param context |
| * @param manager |
| * @return the {@link IEclipsePreferences} node or <code>null</code> |
| */ |
| private IEclipsePreferences getNode(IScopeContext context, IWorkingCopyManager manager) { |
| IEclipsePreferences node = context.getNode(qualifier); |
| if (manager != null) { |
| return manager.getWorkingCopy(node); |
| } |
| return node; |
| } |
| |
| /** |
| * Returns the value stored in the {@link IEclipsePreferences} node from |
| * the given context and working copy manager |
| * |
| * @param context |
| * @param manager |
| * @return the value from the {@link IEclipsePreferences} node or |
| * <code>null</code> |
| */ |
| public String getStoredValue(IScopeContext context, IWorkingCopyManager manager) { |
| IEclipsePreferences node = getNode(context, manager); |
| if (node != null) { |
| return node.get(key, null); |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the stored value of this {@link IEclipsePreferences} node |
| * using a given lookup order, and allowing the top scope to be ignored |
| * |
| * @param lookupOrder |
| * @param ignoreTopScope |
| * @param manager |
| * @return the value from the {@link IEclipsePreferences} node or |
| * <code>null</code> |
| */ |
| public String getStoredValue(IScopeContext[] lookupOrder, boolean ignoreTopScope, IWorkingCopyManager manager) { |
| for (int i = ignoreTopScope ? 1 : 0; i < lookupOrder.length; i++) { |
| String value = getStoredValue(lookupOrder[i], manager); |
| if (value != null) { |
| return value; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Sets the value of this key |
| * |
| * @param context |
| * @param value |
| * @param manager |
| */ |
| public void setStoredValue(IScopeContext context, String value, IWorkingCopyManager manager) { |
| IEclipsePreferences node = getNode(context, manager); |
| if (value != null) { |
| node.put(key, value); |
| } else { |
| node.remove(key); |
| } |
| } |
| |
| @Override |
| public String toString() { |
| return qualifier + '/' + key; |
| } |
| } |
| |
| /** |
| * Returns a new {@link Key} for the {@link ApiUIPlugin} preference store |
| * |
| * @param key |
| * @return the new {@link Key} for the {@link ApiUIPlugin} preference store |
| */ |
| protected final static Key getApiToolsKey(String key) { |
| return new Key(ApiPlugin.PLUGIN_ID, key); |
| } |
| |
| private static final Key KEY_MISSING_DEFAULT_API_PROFILE = getApiToolsKey(IApiProblemTypes.MISSING_DEFAULT_API_BASELINE); |
| |
| private static final Key KEY_PLUGIN_MISSING_IN_BASELINE = getApiToolsKey(IApiProblemTypes.MISSING_PLUGIN_IN_API_BASELINE); |
| /** |
| * An array of all of the keys for the page |
| */ |
| private static Key[] fgAllKeys = { KEY_MISSING_DEFAULT_API_PROFILE, KEY_PLUGIN_MISSING_IN_BASELINE }; |
| |
| /** |
| * Constant representing the severity values presented in the combo boxes |
| * for each option |
| */ |
| private static final String[] SEVERITIES_LABELS = { |
| PreferenceMessages.ApiErrorsWarningsConfigurationBlock_error, |
| PreferenceMessages.ApiErrorsWarningsConfigurationBlock_warning, |
| PreferenceMessages.ApiErrorsWarningsConfigurationBlock_ignore }; |
| |
| /** |
| * Constant representing the severity values presented in the combo boxes |
| * for each option |
| */ |
| private static final String[] SEVERITIES = { |
| ApiPlugin.VALUE_ERROR, ApiPlugin.VALUE_WARNING, |
| ApiPlugin.VALUE_IGNORE, }; |
| |
| /** |
| * Default selection listener for controls on the page |
| */ |
| private SelectionListener selectionlistener = new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| if (e.widget instanceof Combo) { |
| Combo combo = (Combo) e.widget; |
| ControlData data = (ControlData) combo.getData(); |
| data.key.setStoredValue(fLookupOrder[0], combo.getText(), fManager); |
| fDirty = true; |
| ApiBaselinePreferencePage.rebuildcount = 0; |
| } |
| } |
| }; |
| |
| /** |
| * Listing of all of the {@link Combo}s added to the block |
| */ |
| |
| private ArrayList<Combo> fCombos = new ArrayList<>(); |
| /** |
| * Listing of the label in the block |
| */ |
| private ArrayList<Label> fLabels = new ArrayList<>(); |
| |
| /** |
| * The context of settings locations to search for values in |
| */ |
| IScopeContext[] fLookupOrder = null; |
| |
| /** |
| * the working copy manager to work with settings |
| */ |
| IWorkingCopyManager fManager = null; |
| |
| /** |
| * The main composite for the configuration block, used for |
| * enabling/disabling the block |
| */ |
| private Composite fMainComp = null; |
| |
| /** |
| * Flag used to know if the page needs saving or not |
| */ |
| boolean fDirty = false; |
| |
| /** |
| * The parent this block has been added to |
| */ |
| private Composite fParent = null; |
| |
| private boolean hasBaseline = true; |
| /** |
| * Constructor |
| * |
| * @param project |
| */ |
| public ApiBaselinesConfigurationBlock(IWorkbenchPreferenceContainer container) { |
| fLookupOrder = new IScopeContext[] { |
| InstanceScope.INSTANCE, DefaultScope.INSTANCE }; |
| if (container == null) { |
| fManager = new WorkingCopyManager(); |
| } else { |
| fManager = container.getWorkingCopyManager(); |
| } |
| } |
| |
| /** |
| * Creates the control in the parent control |
| * |
| * @param parent the parent control |
| * @param page |
| */ |
| public Control createControl(Composite parent, final ApiBaselinePreferencePage page) { |
| fParent = parent; |
| fMainComp = SWTFactory.createComposite(parent, 1, 1, GridData.FILL_HORIZONTAL, 0, 0); |
| Group optionsProfileGroup = SWTFactory.createGroup(fMainComp, PreferenceMessages.ApiProfilesConfigurationBlock_options_group_title, 2, 1, GridData.FILL_BOTH); |
| Combo combo = createComboControl(optionsProfileGroup, |
| PreferenceMessages.ApiProfilesConfigurationBlock_missing_default_api_profile_message, |
| KEY_MISSING_DEFAULT_API_PROFILE); |
| fCombos.add(combo); |
| combo = createComboControl(optionsProfileGroup, |
| PreferenceMessages.ApiProfilesConfigurationBlock_plugin_missing_in_baseline_message, |
| KEY_PLUGIN_MISSING_IN_BASELINE); |
| fCombos.add(combo); |
| Dialog.applyDialogFont(fMainComp); |
| return fMainComp; |
| } |
| |
| /** |
| * Saves all of the changes on the page |
| */ |
| public void performOK() { |
| save(); |
| } |
| |
| /** |
| * Directly applies all of the changes on the page |
| */ |
| public void performApply() { |
| save(); |
| } |
| |
| /** |
| * Performs the save operation on the working copy manager |
| */ |
| private void save() { |
| if (fDirty) { |
| try { |
| ArrayList<Key> changes = new ArrayList<>(); |
| collectChanges(fLookupOrder[0], changes); |
| filterOutChanges(changes); |
| if (changes.size() == 1 && changes.get(0).equals(KEY_MISSING_DEFAULT_API_PROFILE)) { |
| Key k = changes.get(0); |
| |
| String original = k.getStoredValue(fLookupOrder[0], null); |
| if (original == null) { |
| // this means default value - always error |
| original = ApiPlugin.VALUE_ERROR; |
| } |
| String newval = k.getStoredValue(fLookupOrder[0], fManager); |
| if (newval.equals(ApiPlugin.VALUE_IGNORE)) { |
| // just check for any missing baseline and delete it |
| deleteMissingBaselineMarker(); |
| |
| } else if (newval.equals(ApiPlugin.VALUE_WARNING)) { |
| // if error to warning - change attribute |
| if (original.equals(ApiPlugin.VALUE_ERROR)) { |
| updateMissingBaselineMarkerSeverity(IMarker.SEVERITY_WARNING); |
| } |
| // if ignore to warning - calculate |
| if (original.equals(ApiPlugin.VALUE_IGNORE)) { |
| if (hasBaseline == false) { |
| createMissingBaselineMarker(IMarker.SEVERITY_WARNING); |
| } |
| } |
| |
| } else if (newval.equals(ApiPlugin.VALUE_ERROR)) { |
| // if warning to error - change attribute |
| if (original.equals(ApiPlugin.VALUE_WARNING)) { |
| updateMissingBaselineMarkerSeverity(IMarker.SEVERITY_ERROR); |
| } |
| // if ignore to warning - calculate |
| if (original.equals(ApiPlugin.VALUE_IGNORE)) { |
| if (hasBaseline == false) { |
| createMissingBaselineMarker(IMarker.SEVERITY_ERROR); |
| } |
| } |
| } |
| fDirty = false; |
| return; |
| } |
| // code below probably redundant now |
| if (changes.size() > 0) { |
| if (ApiBaselinePreferencePage.rebuildcount < 1) { |
| ApiBaselinePreferencePage.rebuildcount++; |
| fManager.applyChanges(); |
| String message = PreferenceMessages.ApiErrorsWarningsConfigurationBlock_0; |
| IProject[] apiProjects = Util.getApiProjects(); |
| if (apiProjects != null) { |
| // do not even ask if there are no projects to build |
| int userInput = MessageDialog.open(MessageDialog.QUESTION, fParent.getShell(), |
| PreferenceMessages.ApiErrorsWarningsConfigurationBlock_2, message, SWT.NONE, |
| PreferenceMessages.ApiProfilesPreferencePage_QuestionDialog_buildButtonLabel, |
| PreferenceMessages.ApiProfilesPreferencePage_QuestionDialog_dontBuildButtonLabel); |
| if (Window.OK == userInput) { |
| Util.getBuildJob(apiProjects).schedule(); |
| } |
| } |
| } |
| } |
| fDirty = false; |
| } catch (BackingStoreException bse) { |
| ApiPlugin.log(bse); |
| } |
| } |
| } |
| |
| // Filter out redundant change |
| private void filterOutChanges(ArrayList<Key> changes) { |
| if (changes.size() == 2) { |
| Key k1 = changes.get(0); |
| String original1 = k1.getStoredValue(fLookupOrder[0], null); |
| String newval1 = k1.getStoredValue(fLookupOrder[0], fManager); |
| if (original1 == null && newval1 == null) { |
| changes.remove(0); |
| } |
| if (original1 != null && newval1 != null) { |
| if (original1.equals(newval1)) { |
| changes.remove(0); |
| } |
| } |
| if (changes.size() == 2) { |
| Key k2 = changes.get(1); |
| String original2 = k2.getStoredValue(fLookupOrder[0], null); |
| String newval2 = k2.getStoredValue(fLookupOrder[0], fManager); |
| if (original2 == null && newval2 == null) { |
| changes.remove(1); |
| } |
| if (original2 != null && newval2 != null) { |
| if (original2.equals(newval2)) { |
| changes.remove(1); |
| } |
| } |
| } |
| |
| } |
| } |
| |
| private void updateMissingBaselineMarkerSeverity(int severity) { |
| ArrayList<IMarker> marker = findMissingBaselineMarker(); |
| |
| for (IMarker iMarker : marker) { |
| try { |
| iMarker.setAttribute(IMarker.SEVERITY, severity); |
| } catch (CoreException e) { |
| ApiPlugin.log(e); |
| } |
| } |
| } |
| |
| private void deleteMissingBaselineMarker() { |
| ArrayList<IMarker> marker = findMissingBaselineMarker(); |
| for (IMarker iMarker : marker) { |
| try { |
| iMarker.delete(); |
| } catch (CoreException e) { |
| ApiPlugin.log(e); |
| } |
| } |
| } |
| |
| public void createMissingBaselineMarker() { |
| if (hasBaseline) { |
| return; |
| } |
| String newval = fgAllKeys[0].getStoredValue(fLookupOrder[0], fManager); |
| if (newval == null) { |
| newval = ApiPlugin.VALUE_ERROR; |
| } |
| int valueWarning = -1; |
| if (newval.equals(ApiPlugin.VALUE_WARNING)) { |
| valueWarning = IMarker.SEVERITY_WARNING; |
| } |
| if (newval.equals(ApiPlugin.VALUE_ERROR)) { |
| valueWarning = IMarker.SEVERITY_ERROR; |
| } |
| if(valueWarning < 0) { |
| return; |
| } |
| IProject[] apiProjects = Util.getApiProjects(); |
| if (apiProjects == null) { |
| return; |
| } |
| removeBaselineMismatchMarker(); |
| for (IProject iProject : apiProjects) { |
| createMissingBaselineMarkerOnProject(iProject, valueWarning); |
| } |
| } |
| |
| private void removeBaselineMismatchMarker() { |
| IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); |
| try { |
| IMarker[] findMarkers = root.findMarkers(IApiMarkerConstants.DEFAULT_API_BASELINE_PROBLEM_MARKER, false, |
| IResource.DEPTH_ZERO); |
| for (IMarker iMarker : findMarkers) { |
| iMarker.delete(); |
| } |
| } catch (CoreException e) { |
| ApiPlugin.log(e); |
| } |
| |
| } |
| |
| private void createMissingBaselineMarker(int valueWarning) { |
| IProject[] apiProjects = Util.getApiProjects(); |
| if (apiProjects == null) { |
| return; |
| } |
| for (IProject iProject : apiProjects) { |
| createMissingBaselineMarkerOnProject(iProject, valueWarning); |
| } |
| } |
| |
| private void createMissingBaselineMarkerOnProject(IResource res, int valueWarning) { |
| try { |
| IMarker createMarker = res.createMarker(IApiMarkerConstants.DEFAULT_API_BASELINE_PROBLEM_MARKER); |
| createMarker.setAttribute(IMarker.MESSAGE, PreferenceMessages.ApiBaselinesConfigurationBlock_0); |
| createMarker.setAttribute(IMarker.SEVERITY, valueWarning); |
| } catch (CoreException e) { |
| ApiPlugin.log(e); |
| } |
| } |
| |
| private ArrayList<IMarker> findMissingBaselineMarker() { |
| ArrayList<IMarker> markList = new ArrayList<>(); |
| int missing_plugin = ApiProblemFactory.createProblemId(IApiProblem.CATEGORY_API_BASELINE, |
| IElementDescriptor.RESOURCE, IApiProblem.API_PLUGIN_NOT_PRESENT_IN_BASELINE, IApiProblem.NO_FLAGS); |
| IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); |
| try { |
| IMarker[] findMarkers = root.findMarkers(IApiMarkerConstants.DEFAULT_API_BASELINE_PROBLEM_MARKER, false, |
| IResource.DEPTH_ZERO); |
| for (IMarker iMarker : findMarkers) { |
| markList.add(iMarker); |
| } |
| } catch (CoreException e) { |
| ApiPlugin.log(e); |
| } |
| IProject[] apiProjects = Util.getApiProjects(); |
| if (apiProjects == null) { |
| return markList; |
| } |
| for (IProject iProject : apiProjects) { |
| IMarker[] findMarkers; |
| try { |
| findMarkers = iProject.findMarkers(IApiMarkerConstants.DEFAULT_API_BASELINE_PROBLEM_MARKER, false, |
| IResource.DEPTH_ZERO); |
| for (IMarker iMarker : findMarkers) { |
| int id = ApiProblemFactory.getProblemId(iMarker); |
| if (id == missing_plugin) { |
| continue; |
| } |
| markList.add(iMarker); |
| } |
| } catch (CoreException e) { |
| ApiPlugin.log(e); |
| } |
| } |
| return markList; |
| } |
| |
| /** |
| * Cancels all of the changes on the page |
| */ |
| public void performCancel() { |
| } |
| |
| /** |
| * Reverts all of the settings back to their defaults |
| */ |
| public void performDefaults() { |
| String defval = null; |
| for (Key key : fgAllKeys) { |
| defval = key.getStoredValue(fLookupOrder, true, fManager); |
| key.setStoredValue(fLookupOrder[0], defval, fManager); |
| } |
| updateCombos(); |
| fDirty = true; |
| } |
| |
| /** |
| * Updates all of the registered {@link Combo}s on the page. Registration |
| * implies that the {@link Combo} control was added to the listing of |
| * fCombos |
| */ |
| private void updateCombos() { |
| for (Combo combo : fCombos) { |
| if (combo != null) { |
| ControlData data = (ControlData) combo.getData(); |
| combo.select(data.getSelection(data.getKey().getStoredValue(fLookupOrder, false, fManager))); |
| } |
| |
| } |
| } |
| |
| /** |
| * Disposes the controls from this page |
| */ |
| public void dispose() { |
| fMainComp.getParent().dispose(); |
| } |
| |
| /** |
| * Creates a {@link Label} | {@link Combo} control. The combo is initialised |
| * from the given {@link Key} |
| * |
| * @param parent |
| * @param label |
| * @param key |
| */ |
| protected Combo createComboControl(Composite parent, String label, Key key) { |
| Label lbl = new Label(parent, SWT.NONE); |
| GridData gd = new GridData(GridData.BEGINNING, GridData.CENTER, true, false); |
| lbl.setLayoutData(gd); |
| lbl.setText(label); |
| Combo combo = new Combo(parent, SWT.DROP_DOWN | SWT.READ_ONLY); |
| gd = new GridData(GridData.END, GridData.CENTER, false, false); |
| combo.setLayoutData(gd); |
| ControlData data = new ControlData(key, SEVERITIES); |
| combo.setData(data); |
| combo.setItems(SEVERITIES_LABELS); |
| combo.addSelectionListener(selectionlistener); |
| combo.select(data.getSelection(key.getStoredValue(fLookupOrder, false, fManager))); |
| addHighlight(parent, lbl, combo); |
| fLabels.add(lbl); |
| return combo; |
| } |
| |
| /** |
| * Collects the keys that have changed on the page into the specified list |
| * |
| * @param changes the {@link List} to collect changed keys into |
| */ |
| private void collectChanges(IScopeContext context, List<Key> changes) { |
| String origval = null, newval = null; |
| boolean complete = true; |
| for (Key key : fgAllKeys) { |
| origval = key.getStoredValue(context, null); |
| newval = key.getStoredValue(context, fManager); |
| if (newval == null) { |
| if (origval != null) { |
| changes.add(key); |
| } else if (complete) { |
| key.setStoredValue(context, key.getStoredValue(fLookupOrder, true, fManager), fManager); |
| changes.add(key); |
| } |
| } else if (!newval.equals(origval)) { |
| changes.add(key); |
| } |
| } |
| } |
| |
| public static Key[] getAllKeys() { |
| return fgAllKeys; |
| } |
| |
| public void selectOption(int index) { |
| if (fCombos.get(index) != null && !fCombos.get(index).isDisposed()) { |
| fCombos.get(index).setFocus(); |
| if (fLabels.get(index) != null && !(fLabels.get(index).isDisposed())) { |
| if (org.eclipse.jface.util.Util.isMac()) { |
| if (fLabels.get(index) != null) { |
| highlight(fCombos.get(index).getParent(), fLabels.get(index), fCombos.get(index), |
| ConfigurationBlock.HIGHLIGHT_FOCUS); |
| } |
| } |
| } |
| } |
| } |
| |
| public void setHasBaseline(boolean b) { |
| hasBaseline = b; |
| |
| } |
| } |