package org.bndtools.core.resolve.ui; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.bndtools.core.resolve.ResolutionResult; import org.bndtools.utils.resources.ResourceUtils; import org.eclipse.jface.dialogs.IMessageProvider; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.CheckStateChangedEvent; import org.eclipse.jface.viewers.CheckboxTableViewer; import org.eclipse.jface.viewers.ICheckStateListener; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableItem; import org.eclipse.swt.widgets.Tree; import org.eclipse.ui.forms.widgets.FormToolkit; import org.eclipse.ui.forms.widgets.Section; import org.osgi.framework.Version; import org.osgi.framework.namespace.IdentityNamespace; import org.osgi.resource.Capability; import org.osgi.resource.Namespace; import org.osgi.resource.Requirement; import org.osgi.resource.Resource; import org.osgi.resource.Wire; import aQute.bnd.build.model.BndEditModel; import aQute.bnd.osgi.resource.CapReqBuilder; import aQute.bnd.osgi.resource.Filters; import aQute.libg.filters.AndFilter; import aQute.libg.filters.LiteralFilter; import aQute.libg.filters.SimpleFilter; public class ResolutionSuccessPanel { private final BndEditModel model; private final ResolutionResultPresenter presenter; private final ResolutionTreeContentProvider reasonsContentProvider = new ResolutionTreeContentProvider(); private final List<Resource> checkedOptional = new ArrayList<Resource>(); private final Map<Resource,Requirement> addedOptionals = new HashMap<>(); private Composite composite; private TableViewer requiredViewer; private CheckboxTableViewer optionalViewer; private TreeViewer reasonsViewer; private Button btnAddResolveOptional; private ResolutionResult result; private Section sectOptional; public ResolutionSuccessPanel(BndEditModel model, ResolutionResultPresenter presenter) { this.model = model; this.presenter = presenter; } public void createControl(Composite parent) { FormToolkit toolkit = new FormToolkit(parent.getDisplay()); composite = toolkit.createComposite(parent); GridLayout layout = new GridLayout(1, false); composite.setLayout(layout); SashForm form = new SashForm(composite, SWT.VERTICAL); form.setLayout(new GridLayout(1, false)); form.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); GridData gd; Section sectRequired = toolkit.createSection(form, Section.TITLE_BAR | Section.EXPANDED); sectRequired.setText("Required Resources"); gd = new GridData(SWT.FILL, SWT.FILL, true, true); gd.widthHint = 200; gd.heightHint = 150; sectRequired.setLayoutData(gd); Table tblRequired = toolkit.createTable(sectRequired, SWT.BORDER | SWT.FULL_SELECTION | SWT.H_SCROLL | SWT.V_SCROLL); sectRequired.setClient(tblRequired); requiredViewer = new TableViewer(tblRequired); requiredViewer.setContentProvider(ArrayContentProvider.getInstance()); requiredViewer.setLabelProvider(new ResourceLabelProvider()); requiredViewer.setSorter(new BundleSorter()); requiredViewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { IStructuredSelection sel = (IStructuredSelection) event.getSelection(); Resource resource = (Resource) sel.getFirstElement(); reasonsContentProvider.setOptional(false); if (result != null) reasonsContentProvider.setResolution(result.getResourceWirings()); reasonsViewer.setInput(resource); reasonsViewer.expandToLevel(2); } }); sectOptional = toolkit.createSection(form, Section.TITLE_BAR | Section.TWISTIE | Section.EXPANDED); sectOptional.setText("Optional Resources"); Composite cmpOptional = toolkit.createComposite(sectOptional); sectOptional.setClient(cmpOptional); cmpOptional.setLayout(new GridLayout(2, false)); gd = new GridData(SWT.FILL, SWT.FILL, true, true); gd.widthHint = 200; gd.heightHint = 150; sectOptional.setLayoutData(gd); Table tblOptional = toolkit.createTable(cmpOptional, SWT.BORDER | SWT.CHECK | SWT.FULL_SELECTION | SWT.H_SCROLL | SWT.V_SCROLL); gd = new GridData(SWT.FILL, SWT.FILL, true, true); tblOptional.setLayoutData(gd); optionalViewer = new CheckboxTableViewer(tblOptional); optionalViewer.setContentProvider(ArrayContentProvider.getInstance()); optionalViewer.setLabelProvider(new ResourceLabelProvider()); optionalViewer.setSorter(new BundleSorter()); optionalViewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { IStructuredSelection sel = (IStructuredSelection) event.getSelection(); doOptionalReasonUpdate((Resource) sel.getFirstElement()); } }); optionalViewer.addCheckStateListener(new ICheckStateListener() { @Override public void checkStateChanged(CheckStateChangedEvent event) { Resource resource = (Resource) event.getElement(); if (!addedOptionals.containsKey(resource)) { if (event.getChecked()) { checkedOptional.add(resource); } else { checkedOptional.remove(resource); } } presenter.updateButtons(); updateResolveOptionalButton(); optionalViewer.setSelection(new ISelection() { @Override public boolean isEmpty() { return true; } }); doOptionalReasonUpdate(resource); } }); Composite cmpOptionalButtons = toolkit.createComposite(cmpOptional); cmpOptionalButtons.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1)); GridLayout gl_cmpOptionalButtons = new GridLayout(3, false); gl_cmpOptionalButtons.marginHeight = 0; gl_cmpOptionalButtons.marginWidth = 0; cmpOptionalButtons.setLayout(gl_cmpOptionalButtons); btnAddResolveOptional = toolkit.createButton(cmpOptionalButtons, "Update and Resolve", SWT.NONE); btnAddResolveOptional.setEnabled(false); btnAddResolveOptional.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { doAddResolve(); } }); btnAddResolveOptional.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, true, false)); Button btnAllOptional = toolkit.createButton(cmpOptionalButtons, "Select All", SWT.NONE); btnAllOptional.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { optionalViewer.setAllChecked(true); checkedOptional.clear(); for (Object object : optionalViewer.getCheckedElements()) { if (!addedOptionals.containsKey(object)) { checkedOptional.add((Resource) object); } } presenter.updateButtons(); updateResolveOptionalButton(); } }); btnAllOptional.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); Button btnClearOptional = toolkit.createButton(cmpOptionalButtons, "Clear All", SWT.NONE); btnClearOptional.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { optionalViewer.setAllChecked(false); checkedOptional.clear(); presenter.updateButtons(); updateResolveOptionalButton(); } }); btnClearOptional.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); Section sectReason = toolkit.createSection(form, Section.TITLE_BAR | Section.TWISTIE | Section.EXPANDED); sectReason.setText("Reasons"); Tree tblReasons = new Tree(sectReason, SWT.BORDER | SWT.FULL_SELECTION | SWT.H_SCROLL | SWT.V_SCROLL); sectReason.setClient(tblReasons); gd = new GridData(SWT.FILL, SWT.FILL, true, true); gd.widthHint = 200; gd.heightHint = 150; sectReason.setLayoutData(gd); reasonsViewer = new TreeViewer(tblReasons); reasonsViewer.setContentProvider(reasonsContentProvider); reasonsViewer.setLabelProvider(new ResolutionTreeLabelProvider()); } public Control getControl() { return composite; } private void doAddResolve() { List<Requirement> oldRequires = model.getRunRequires(); if (oldRequires == null) oldRequires = Collections.emptyList(); ArrayList<Requirement> newRequires = new ArrayList<Requirement>(oldRequires.size() + checkedOptional.size()); newRequires.addAll(oldRequires); for (Iterator<Entry<Resource,Requirement>> it = addedOptionals.entrySet().iterator(); it.hasNext();) { Entry<Resource,Requirement> entry = it.next(); if (!optionalViewer.getChecked(entry.getKey())) { newRequires.remove(entry.getValue()); it.remove(); } } for (Resource resource : checkedOptional) { Requirement req = resourceToRequirement(resource); addedOptionals.put(resource, req); newRequires.add(req); } model.setRunRequires(newRequires); checkedOptional.clear(); presenter.recalculate(); } public void setInput(ResolutionResult result) { this.result = result; checkedOptional.clear(); Set<Resource> wirings = (result != null && result.getResourceWirings() != null) ? result.getResourceWirings().keySet() : null; requiredViewer.setInput(wirings != null ? wirings : null); wirings = (result != null && result.getOptionalResources() != null) ? new HashSet<Resource>(result.getOptionalResources().keySet()) : new HashSet<Resource>(); wirings.addAll(addedOptionals.keySet()); if (wirings.isEmpty()) { sectOptional.setExpanded(false); } else { sectOptional.setExpanded(true); } optionalViewer.setInput(wirings.isEmpty() ? null : wirings); optionalViewer.setGrayedElements(addedOptionals.keySet().toArray()); optionalViewer.setCheckedElements(addedOptionals.keySet().toArray()); for (TableItem tableItem : optionalViewer.getTable().getItems()) { Display display = Display.getCurrent(); Color addedColor = display.getSystemColor(SWT.COLOR_GRAY); if (tableItem.getGrayed()) { tableItem.setBackground(addedColor); } else { tableItem.setBackground(null); } } updateResolveOptionalButton(); } private void updateResolveOptionalButton() { if (!checkedOptional.isEmpty() || previouslyAddedNowRemoved()) { btnAddResolveOptional.setEnabled(true); presenter.setMessage("Click 'Update and Resolve' to add the checked optional bundles to requirements and re-resolve.", IMessageProvider.INFORMATION); } else { btnAddResolveOptional.setEnabled(false); presenter.setMessage(null, IMessageProvider.INFORMATION); } } private boolean previouslyAddedNowRemoved() { for (Resource resource : addedOptionals.keySet()) { if (!optionalViewer.getChecked(resource)) { return true; } } return false; } public boolean isComplete() { return checkedOptional.isEmpty(); } public void dispose() {} private void doOptionalReasonUpdate(Resource resource) { reasonsContentProvider.setOptional(true); if (result != null) { Map<Resource,List<Wire>> combined = new HashMap<Resource,List<Wire>>(result.getResourceWirings()); combined.putAll(result.getOptionalResources()); reasonsContentProvider.setResolution(combined); } reasonsViewer.setInput(resource); reasonsViewer.expandToLevel(2); } private static Requirement resourceToRequirement(Resource resource) { Capability identity = ResourceUtils.getIdentityCapability(resource); String id = ResourceUtils.getIdentity(identity); Version version = ResourceUtils.getVersion(identity); Version dropQualifier = new Version(version.getMajor(), version.getMinor(), version.getMicro()); AndFilter filter = new AndFilter(); filter.addChild(new SimpleFilter(IdentityNamespace.IDENTITY_NAMESPACE, id)); filter.addChild(new LiteralFilter(Filters.fromVersionRange(dropQualifier.toString()))); Requirement req = new CapReqBuilder(IdentityNamespace.IDENTITY_NAMESPACE).addDirective(Namespace.REQUIREMENT_FILTER_DIRECTIVE, filter.toString()).buildSyntheticRequirement(); return req; } }