blob: b0f24b27a2e8840a080150e02304eb04e484a902 [file] [log] [blame]
/*******************************************************************************
* 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;
}
}