blob: b1751aa1f30e024a15bb240d4f7b24216f2ed880 [file] [log] [blame]
/**
********************************************************************************
* Copyright (c) 2019-2021 Robert Bosch GmbH 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/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Robert Bosch GmbH - initial API and implementation
********************************************************************************
*/
package org.eclipse.app4mc.validation.ui;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.app4mc.validation.ui.provider.ProfileContentProvider;
import org.eclipse.app4mc.validation.ui.provider.ProfileLabelProvider;
import org.eclipse.app4mc.validation.util.CachedProfile;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
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.swt.widgets.Shell;
import org.eclipse.ui.dialogs.CheckedTreeSelectionDialog;
public class ProfileDialog extends CheckedTreeSelectionDialog {
private ProfileDialogSettings settings;
private Button btnScopeFile;
private Button btnScopeFolder;
public ProfileDialog(Shell parentShell, ProfileDialogSettings set) {
this(parentShell, new ProfileLabelProvider(), new ProfileContentProvider(), set);
}
public ProfileDialog(Shell parentShell, ILabelProvider labelProvider, ITreeContentProvider contentProvider, ProfileDialogSettings set) {
super(parentShell, labelProvider, contentProvider);
this.settings = (set != null) ? set : new ProfileDialogSettings();
initializeDialog();
}
@Override
protected void setReturnCode(int code) {
// handle settings - save UI content
if (code == Window.OK) {
settings.setDialogSuccess(true);
saveDialogResults();
}
super.setReturnCode(code);
}
@Override
protected Control createDialogArea(Composite parent) {
Composite composite = (Composite) super.createDialogArea(parent);
// create scope buttons
Group grpScope = new Group(composite, SWT.NONE);
grpScope.setText("Scope");
grpScope.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
grpScope.setLayout(new RowLayout(SWT.HORIZONTAL));
btnScopeFile = new Button(grpScope, SWT.RADIO);
btnScopeFile.setText("Current file");
btnScopeFolder = new Button(grpScope, SWT.RADIO);
btnScopeFolder.setText("Folder");
// set selection of scope buttons
String scope = settings.getScope();
btnScopeFile.setSelection(scope.equals(ProfileDialogSettings.SCOPE_FILE));
btnScopeFolder.setSelection(scope.equals(ProfileDialogSettings.SCOPE_FOLDER));
return composite;
}
@Override
protected Label createMessageArea(Composite composite) {
Label label = new Label(composite, SWT.WRAP);
if (getMessage() != null) {
label.setText(getMessage());
}
label.setFont(composite.getFont());
GridData gd = new GridData(SWT.FILL, SWT.FILL, true, false);
gd.widthHint = 300;
label.setLayoutData(gd);
return label;
}
@Override
protected CheckboxTreeViewer createTreeViewer(Composite parent) {
CheckboxTreeViewer treeViewer = super.createTreeViewer(parent);
ColumnViewerToolTipSupport.enableFor(treeViewer);
return treeViewer;
}
private void initializeDialog() {
this.setContainerMode(true);
this.setMessage("Choose profiles");
this.setTitle("Validations");
this.setInput(settings);
// set initial selection
List<CachedProfile> initialSelection = this.settings.getInitialSelection();
setInitialElementSelections(initialSelection);
setExpandedElements(initialSelection.stream().flatMap(cached -> getParents(cached).stream()).toArray());
}
private void saveDialogResults() {
Object[] dialogResults = getResult();
if (dialogResults == null) {
return;
}
// profiles
settings.setDialogResults(filterProfiles(dialogResults).stream()
.map(CachedProfile::getProfileClass)
.collect(Collectors.toList()));
// scope
if (btnScopeFile.getSelection()) {
settings.setScope(ProfileDialogSettings.SCOPE_FILE);
} else if (btnScopeFolder.getSelection()) {
settings.setScope(ProfileDialogSettings.SCOPE_FOLDER);
}
}
// some helper methods
private static List<CachedProfile> filterProfiles(Object[] dialogResults) {
List<CachedProfile> selectedNodes = Arrays.stream(dialogResults)
.map(CachedProfile.class::cast)
.collect(Collectors.toList());
HashSet<CachedProfile> nodesToRemove = new HashSet<>();
// 2. bottom up: parents with partially selected children must be removed
for (CachedProfile node : selectedNodes) {
if (!node.getCachedProfiles().isEmpty() && !nodesToRemove.contains(node)) {
Collection<CachedProfile> cachedProfiles = node.getCachedProfiles().values();
if (cachedProfiles.stream().anyMatch(c -> !selectedNodes.contains(c))) {
nodesToRemove.add(node);
nodesToRemove.addAll(getParents(node));
}
}
}
// 3. top down: if node is selected then all sub nodes can be removed
for (CachedProfile node : selectedNodes) {
if (!node.getCachedProfiles().isEmpty() && !nodesToRemove.contains(node)) {
nodesToRemove.addAll(getChildren(node));
}
}
// finally remove the nodes
selectedNodes.removeAll(nodesToRemove);
return selectedNodes;
}
private static List<CachedProfile> getParents(CachedProfile node) {
ArrayList<CachedProfile> list = new ArrayList<>();
for (CachedProfile parent = node.getParentProfile(); parent != null; parent = parent.getParentProfile()) {
list.add(parent);
}
return list;
}
private static List<CachedProfile> getChildren(CachedProfile node) {
ArrayList<CachedProfile> list = new ArrayList<>();
collectChildren(node, list);
return list;
}
private static void collectChildren(CachedProfile node, Collection<CachedProfile> collection) {
if (!node.getCachedProfiles().isEmpty()) {
for (CachedProfile child : node.getCachedProfiles().values()) {
collection.add(child);
collectChildren(child, collection);
}
}
}
}