/******************************************************************************* * Copyright (c) 2004, 2006 * Thomas Hallgren, Kenneth Olwing, Mitch Sonies * Pontus Rydin, Nils Unden, Peer Torngren * The code, documentation and other materials contained herein have been * licensed under the Eclipse Public License - v 1.0 by the individual * copyright holders listed above, as Initial Contributors under such license. * The text of such license is available at www.eclipse.org. *******************************************************************************/ package org.eclipse.buckminster.ui.wizards; import java.lang.reflect.InvocationTargetException; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.TreeMap; import org.eclipse.buckminster.core.CorePlugin; import org.eclipse.buckminster.core.cspec.ICSpecData; import org.eclipse.buckminster.core.cspec.model.ComponentRequest; import org.eclipse.buckminster.core.metadata.IResolution; import org.eclipse.buckminster.core.metadata.model.BOMNode; import org.eclipse.buckminster.core.metadata.model.BillOfMaterials; import org.eclipse.buckminster.core.metadata.model.Resolution; import org.eclipse.buckminster.core.metadata.model.UnresolvedNode; import org.eclipse.buckminster.core.mspec.model.MaterializationSpec; import org.eclipse.buckminster.core.reader.IReaderType; import org.eclipse.buckminster.core.resolver.IResolver; import org.eclipse.buckminster.core.resolver.MainResolver; import org.eclipse.buckminster.core.resolver.ResolutionContext; import org.eclipse.buckminster.core.version.VersionHelper; import org.eclipse.buckminster.runtime.BuckminsterException; import org.eclipse.buckminster.ui.Messages; import org.eclipse.buckminster.ui.SaveRunnable; import org.eclipse.buckminster.ui.UiPlugin; import org.eclipse.buckminster.ui.UiUtils; import org.eclipse.buckminster.ui.internal.DynamicTableLayout; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.equinox.p2.metadata.VersionRange; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.ColumnWeightData; import org.eclipse.jface.viewers.ITableLabelProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.window.Window; import org.eclipse.jface.wizard.IWizardContainer; import org.eclipse.osgi.util.NLS; 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.Font; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.Image; 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.Display; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.ui.dialogs.SaveAsDialog; /** * @author Kenneth Olwing * @author Thomas Hallgren */ public class ResolverNodePage extends AbstractQueryPage { class RequestLabelProvider extends LabelProvider implements ITableLabelProvider { @Override public Image getColumnImage(Object element, int columnIndex) { return null; } @Override public String getColumnText(Object element, int columnIndex) { ComponentRequest rq = (ComponentRequest) element; String lbl; switch (columnIndex) { case 0: lbl = rq.getName(); break; case 1: VersionRange vd = rq.getVersionRange(); lbl = vd == null ? "" : VersionHelper.getHumanReadable(vd); //$NON-NLS-1$ break; default: lbl = rq.getComponentTypeID(); } return lbl; } } TableViewer dependenciesTable; private Group detailGroup; private Image grayDotImage; private Image grayDotWithRedExclamationImage; private Image greenDotImage; private Font itemItalicFont; private final HashMap<Resolution, Integer> masterDups = new HashMap<Resolution, Integer>(); private Tree masterTree; private Composite masterTreeComposite; private final HashMap<Resolution, BOMNode> parentWhenFirstSeen = new HashMap<Resolution, BOMNode>(); private Image redDotImage; private Button reresolveButton; private Button showTargetPlatformButton; private Button unresolveButton; private Image yellowDotImage; public ResolverNodePage() { super(""); //$NON-NLS-1$ redDotImage = UiPlugin.getImageDescriptor("images/red_dot_16x16.bmp").createImage(); //$NON-NLS-1$ greenDotImage = UiPlugin.getImageDescriptor("images/green_dot_16x16.bmp").createImage(); //$NON-NLS-1$ yellowDotImage = UiPlugin.getImageDescriptor("images/yellow_dot_16x16.bmp").createImage(); //$NON-NLS-1$ grayDotImage = UiPlugin.getImageDescriptor("images/gray_dot_16x16.bmp").createImage(); //$NON-NLS-1$ grayDotWithRedExclamationImage = UiPlugin.getImageDescriptor("images/gray_dot_with_red_exclamation_16x16.bmp").createImage(); //$NON-NLS-1$ setDescription(Messages.resolution_tree); } @Override public Composite createControls(Composite parent) { Composite topComposite = new Composite(parent, SWT.NONE); topComposite.setLayout(new GridLayout()); topComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); SashForm form = new SashForm(topComposite, SWT.VERTICAL); form.setLayout(new GridLayout()); form.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); createMasterGroup(form); createDetailGroup(form); form.setWeights(new int[] { 60, 40 }); createButtonGroup(topComposite); return topComposite; } @Override public void dispose() { redDotImage.dispose(); redDotImage = null; greenDotImage.dispose(); greenDotImage = null; grayDotImage.dispose(); grayDotImage = null; grayDotWithRedExclamationImage.dispose(); grayDotWithRedExclamationImage = null; yellowDotImage.dispose(); yellowDotImage = null; if (itemItalicFont != null) itemItalicFont.dispose(); itemItalicFont = null; super.dispose(); } @Override protected void pageIsShowing() { super.pageIsShowing(); resetMaster(); BOMNode currentNode = getSelectedMasterNode(); TreeItem found = null; for (TreeItem child : masterTree.getItems()) { TreeItem ti = findItemWithData(child, currentNode); if (ti != null) { found = ti; break; } } if (found == null) found = masterTree.getItems()[0]; masterTree.setSelection(new TreeItem[] { found }); masterTreeSelectionEvent(found, false); updatePageCompletion(); getControl().setVisible(true); } void masterTreeSelectionEvent(TreeItem ti, boolean checkEvent) { try { BOMNode node = (BOMNode) ti.getData(); IResolution resolution = node.getResolution(); boolean isResolved = (resolution != null); if (isResolved) { ICSpecData cspec = resolution.getCSpec(); dependenciesTable.setInput(cspec.getDependencies()); detailGroup.setText(NLS.bind(Messages.dependencies_in_0, node.getViewName())); } else { dependenciesTable.setInput(null); detailGroup.setText(""); //$NON-NLS-1$ } BillOfMaterials bom = getQueryWizard().getBOM(); reresolveButton.setEnabled(!bom.isFullyResolved(getContext())); unresolveButton.setEnabled(isResolved); } catch (CoreException e) { displayException(e); } } void reresolve() { try { QueryWizard wizard = getQueryWizard(); BillOfMaterials bom = wizard.getBOM(); ResolutionContext context = new ResolutionContext(bom.getQuery()); context.setContinueOnError(true); final BillOfMaterials[] bin = new BillOfMaterials[] { bom }; final IResolver resolver = new MainResolver(context); wizard.getContainer().run(true, true, new IRunnableWithProgress() { @Override public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { try { BillOfMaterials oldBOM = bin[0]; BillOfMaterials newBOM = oldBOM.fullyResolve(resolver, monitor); if (oldBOM.equals(newBOM)) bin[0] = null; else bin[0] = newBOM; } catch (Throwable t) { throw new InvocationTargetException(t); } } }); CorePlugin.logWarningsAndErrors(context.getStatus()); bom = bin[0]; if (bom != null) { wizard.setBOM(bom); resetMaster(); } } catch (Throwable t) { displayException(BuckminsterException.wrap(t)); } } void resetMaster() { QueryWizard wizard = getQueryWizard(); BillOfMaterials bom = wizard.getBOM(); try { masterDups.clear(); countDuplicates(null, bom, wizard.getMaterializationContext().getMaterializationSpec()); masterTree.removeAll(); masterTree.clearAll(false); addMasterItem(new TreeItem(masterTree, SWT.NONE), null, bom); reresolveButton.setEnabled(!bom.isFullyResolved(wizard.getContext())); updatePageCompletion(); } catch (CoreException e) { displayException(e); } } void saveBOMInFileSystem() { FileDialog dlg = new FileDialog(getShell(), SWT.SAVE); dlg.setFilterExtensions(new String[] { "*.bom" }); //$NON-NLS-1$ String location = dlg.open(); if (location == null) return; saveToPath(new Path(location)); } void saveBOMInWorkspace() { SaveAsDialog dialog = new SaveAsDialog(getShell()); if (dialog.open() == Window.CANCEL) return; IPath filePath = dialog.getResult(); if (filePath == null) return; IWorkspace workspace = ResourcesPlugin.getWorkspace(); IFile file = workspace.getRoot().getFile(filePath); saveToPath(file.getLocation()); } void unresolveNode() { final BOMNode node = getSelectedMasterNode(); if (node == null || node.getResolution() == null) return; QueryWizard queryWizard = getQueryWizard(); try { queryWizard.setBOM(queryWizard.getBOM().replaceNode(new UnresolvedNode(node.getQualifiedDependency()))); } catch (CoreException e) { displayException(e); } resetMaster(); } private void addMasterItem(TreeItem ti, BOMNode parent, BOMNode node) throws CoreException { ti.removeAll(); ti.setText(node.getViewName()); ti.setData(node); IResolution resolution = node.getResolution(); if (resolution == null) { if (node.isFullyResolved(getContext().getComponentQuery(), getContext())) ti.setImage(yellowDotImage); else ti.setImage(redDotImage); ti.setFont(null); return; } Integer nc = masterDups.get(resolution); if (nc != null && nc.intValue() > 1 && !wasParentWhenFirstSeen(parent, node)) { ti.setImage(grayDotImage); ti.setFont(itemItalicFont); return; } ti.setImage(greenDotImage); ti.setFont(null); boolean showPlatformTargets = showTargetPlatformButton.getSelection(); for (BOMNode child : getSortedChildren(node)) { IResolution ci = child.getResolution(); if (!showPlatformTargets && ci != null) { if (IReaderType.ECLIPSE_PLATFORM.equals(ci.getProvider().getReaderTypeId())) continue; } addMasterItem(new TreeItem(ti, SWT.NONE), node, child); } ti.setExpanded(true); } private void countDuplicates(BOMNode parent, BOMNode node, MaterializationSpec mspec) throws CoreException { Resolution ci = node.getResolution(); if (ci == null) return; Integer nc = masterDups.get(ci); int nci; if (nc == null) { nci = 0; if (parent != null) parentWhenFirstSeen.put(ci, parent); } else nci = nc.intValue(); masterDups.put(ci, new Integer(1 + nci)); if (nci == 0) { for (BOMNode child : getSortedChildren(node)) countDuplicates(node, child, mspec); } } private void createButtonGroup(Composite parent) { Composite buttons = new Composite(parent, SWT.NONE); buttons.setLayout(new GridLayout(3, false)); buttons.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, true, false)); Button saveButton = UiUtils.createPushButton(buttons, Messages.save_bom, new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { saveBOMInWorkspace(); } }); saveButton.setLayoutData(new GridData(SWT.TRAIL, SWT.TOP, true, false)); Button extSaveButton = UiUtils.createPushButton(buttons, Messages.external_save_bom, new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { saveBOMInFileSystem(); } }); extSaveButton.setLayoutData(new GridData(SWT.TRAIL, SWT.TOP, false, false)); } private void createDetailGroup(Composite parent) { detailGroup = new Group(parent, SWT.NONE); detailGroup.setLayout(new GridLayout()); GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true); gd.heightHint = 100; detailGroup.setLayoutData(gd); Table table = new Table(detailGroup, SWT.BORDER | SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL); String[] columnNames = new String[] { Messages.name, Messages.version_designator, Messages.category }; int[] columnWeights = new int[] { 20, 10, 10 }; table.setHeaderVisible(true); DynamicTableLayout layout = new DynamicTableLayout(150); for (int idx = 0; idx < columnNames.length; idx++) { TableColumn tableColumn = new TableColumn(table, SWT.LEFT, idx); tableColumn.setText(columnNames[idx]); layout.addColumnData(new ColumnWeightData(columnWeights[idx], true)); } table.setLayout(layout); table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); dependenciesTable = new TableViewer(table); dependenciesTable.setLabelProvider(new RequestLabelProvider()); dependenciesTable.setContentProvider(new ArrayContentProvider()); } private void createMasterGroup(Composite parent) { Group masterGroup = new Group(parent, SWT.NONE); masterGroup.setLayout(new GridLayout(3, false)); masterGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); masterGroup.setText(Messages.component_specification_selection); masterTreeComposite = new Composite(masterGroup, SWT.NONE); masterTreeComposite.setLayout(new GridLayout()); GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true); gd.heightHint = 100; gd.horizontalSpan = 3; masterTreeComposite.setLayoutData(gd); masterTree = new Tree(masterTreeComposite, SWT.NONE | SWT.SINGLE | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL); masterTree.setLayout(new GridLayout()); masterTree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); masterTree.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent se) { masterTreeSelectionEvent((TreeItem) se.item, false); } }); Font f = masterTreeComposite.getFont(); FontData[] fda = f.getFontData(); for (FontData fd : fda) fd.setStyle(SWT.ITALIC); itemItalicFont = new Font(Display.getCurrent(), fda); Composite modifiersComposite = new Composite(masterGroup, SWT.NONE); modifiersComposite.setLayout(new GridLayout(3, false)); modifiersComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); showTargetPlatformButton = UiUtils.createCheckButton(modifiersComposite, Messages.show_target_platform_components, new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent se) { resetMaster(); } }); reresolveButton = UiUtils.createPushButton(modifiersComposite, Messages.re_resolve, new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent se) { reresolve(); } }); unresolveButton = UiUtils.createPushButton(modifiersComposite, Messages.unresolved_node, new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent se) { unresolveNode(); } }); } private TreeItem findItemWithData(TreeItem ti, BOMNode node) { if (ti.getData() == node) return ti; for (TreeItem child : ti.getItems()) { TreeItem answer = findItemWithData(child, node); if (answer != null) return answer; } return null; } private BOMNode getSelectedMasterNode() { TreeItem[] tia = masterTree.getSelection(); return (tia.length > 0) ? (BOMNode) tia[0].getData() : null; } private Collection<BOMNode> getSortedChildren(BOMNode parent) throws CoreException { Collection<BOMNode> children = parent.getChildren(); if (children.size() > 1) { Map<String, BOMNode> sortedMap = new TreeMap<String, BOMNode>(); for (BOMNode child : children) sortedMap.put(child.getViewName(), child); children = sortedMap.values(); } return children; } private void saveToPath(IPath path) { QueryWizard wizard = getQueryWizard(); IWizardContainer container = wizard.getContainer(); try { SaveRunnable sr = new SaveRunnable(wizard.getBOM(), path); container.run(true, true, sr); wizard.getMaterializationSpec().setURL(path.toFile().toURI().toString()); } catch (InterruptedException e) { } catch (Exception e) { CoreException t = BuckminsterException.wrap(e); String msg = NLS.bind(Messages.unable_to_save_file_0, path); CorePlugin.getLogger().error(t, msg); ErrorDialog.openError(getShell(), null, msg, t.getStatus()); } } private void updatePageCompletion() { boolean complete; String errorMsg = null; QueryWizard qw = getQueryWizard(); BillOfMaterials bom = qw.getBOM(); try { complete = (bom != null); if (complete) { if (!bom.isFullyResolved(qw.getContext())) complete = qw.getContext().isContinueOnError(); } } catch (CoreException e) { complete = false; displayException(e); } if (!complete) errorMsg = Messages.a_selected_specification_is_unresolved; setPageComplete(complete); setErrorMessage(errorMsg); } private boolean wasParentWhenFirstSeen(BOMNode parent, BOMNode node) throws CoreException { if (parent == null) // // Top node is never a duplicate // return true; IResolution resolution = node.getResolution(); return (resolution == null) ? false : parentWhenFirstSeen.get(resolution) == parent; } }