/******************************************************************************* * Copyright (c) 2015, 2017 Red Hat Inc. and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Red Hat - Initial Contribution *******************************************************************************/ package org.eclipse.linuxtools.internal.docker.ui.wizards; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import org.eclipse.core.databinding.DataBindingContext; import org.eclipse.core.databinding.beans.BeanProperties; import org.eclipse.core.databinding.observable.list.IObservableList; import org.eclipse.jface.databinding.viewers.ObservableListContentProvider; import org.eclipse.jface.databinding.viewers.ViewerProperties; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.viewers.CellLabelProvider; import org.eclipse.jface.viewers.ColumnLabelProvider; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.jface.wizard.WizardPage; import org.eclipse.linuxtools.docker.core.DockerException; import org.eclipse.linuxtools.docker.core.IDockerConnection; import org.eclipse.linuxtools.docker.core.IRegistry; import org.eclipse.linuxtools.docker.core.IRepositoryTag; import org.eclipse.linuxtools.docker.ui.Activator; import org.eclipse.linuxtools.internal.docker.core.RepositoryTag; import org.eclipse.linuxtools.internal.docker.core.RepositoryTagV2; import org.eclipse.linuxtools.internal.docker.ui.SWTImagesFactory; import org.eclipse.linuxtools.internal.docker.ui.wizards.ImageSearchPage.IconColumnLabelProvider; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; /** * {@link WizardPage} to select an existing tag for a given image (repository) * on a remote registry. This page performs a call to the registry when it is * activated. */ public class ImageTagSelectionPage extends WizardPage { private final ImageSearchModel model; private final DataBindingContext ctx = new DataBindingContext(); private IRegistry registry; /** * Default constructor. * * @param model * the model associated to this page * @param registry * the registry on which the search is performed */ public ImageTagSelectionPage(final ImageSearchModel model, final IRegistry registry) { super("ImageTagSelectionPage", //$NON-NLS-1$ WizardMessages.getString("ImageTagSelectionPage.title"), //$NON-NLS-1$ SWTImagesFactory.DESC_BANNER_REPOSITORY); setMessage(WizardMessages.getString("ImageTagSelectionPage.title")); //$NON-NLS-1$ this.model = model; this.registry = registry; } @Override public void setVisible(boolean visible) { super.setVisible(visible); if (visible) { searchTags(); } } private void searchTags() { try { final BlockingQueue<List<DockerImageTagSearchResult>> searchResultQueue = new ArrayBlockingQueue<>( 1); ImageTagSelectionPage.this.getContainer().run(true, true, monitor -> { monitor.beginTask( WizardMessages.getString( "ImageTagSelectionPage.searchTask"), //$NON-NLS-1$ 2); final String selectedImageName = ImageTagSelectionPage.this.model .getSelectedImage().getName(); try { final List<IRepositoryTag> repositoryTags = registry .getTags(selectedImageName); // we have to convert to list of RepositoryTag which // can be sorted final List<RepositoryTag> tags = repositoryTags .stream() .map(c -> (RepositoryTag) c) .collect(Collectors.toList()); Collections.sort(tags); monitor.worked(1); final IDockerConnection connection = model .getSelectedConnection(); final List<DockerImageTagSearchResult> searchResults = repositoryTags .stream() .map(t -> new DockerImageTagSearchResult( selectedImageName, t, connection.hasImage( selectedImageName, t.getName()))) .collect(Collectors.toList()); monitor.worked(1); searchResultQueue.offer(searchResults); } catch (DockerException e) { } finally { monitor.done(); } }); List<DockerImageTagSearchResult> res = searchResultQueue.poll(10, TimeUnit.SECONDS); final List<DockerImageTagSearchResult> searchResult = (res == null) ? new ArrayList<>() : res; Display.getCurrent().asyncExec(() -> { ImageTagSelectionPage.this.model .setImageTagSearchResult(searchResult); // refresh the wizard buttons getWizard().getContainer().updateButtons(); }); // display a warning in the title area if the search result is empty if (searchResult.isEmpty()) { this.setMessage( WizardMessages.getString( "ImageTagSelectionPage.noTagWarning"), //$NON-NLS-1$ WARNING); } else if (searchResult.size() == 1) { this.setMessage( WizardMessages.getString( "ImageTagSelectionPage.oneTagMatched"), //$NON-NLS-1$ INFORMATION); } else { this.setMessage( WizardMessages.getFormattedString( "ImageTagSelectionPage.tagsMatched", //$NON-NLS-1$ Integer.toString(searchResult.size())), INFORMATION); } } catch (InvocationTargetException | InterruptedException e) { Activator.log(e); } } /** * @return the selected tag in the search result table */ public DockerImageTagSearchResult getSelectedImageTag() { return model.getSelectedImageTag(); } @Override public boolean isPageComplete() { return this.model.getSelectedImageTag() != null; } @Override public void dispose() { ctx.dispose(); super.dispose(); } @SuppressWarnings("unchecked") @Override public void createControl(final Composite parent) { final int COLUMNS = 1; final Composite container = new Composite(parent, SWT.NONE); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).span(1, 1) .grab(true, false).applyTo(container); GridLayoutFactory.fillDefaults().numColumns(COLUMNS).margins(6, 6) .spacing(10, 2).applyTo(container); // tags/layers table final Table table = new Table(container, SWT.BORDER | SWT.FULL_SELECTION | SWT.V_SCROLL | SWT.H_SCROLL); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL) .grab(true, true).span(COLUMNS, 1).hint(200, 200) .applyTo(table); final TableViewer tableViewer = new TableViewer(table); table.setHeaderVisible(true); table.setLinesVisible(true); addTableViewerColum(tableViewer, WizardMessages.getString("ImageTagSelectionPage.column.tag"), //$NON-NLS-1$ SWT.NONE, SWT.LEFT, 200, new RepositoryTagColumnLabelProvider()); addTableViewerColum(tableViewer, WizardMessages.getString("ImageTagSelectionPage.column.layer"), //$NON-NLS-1$ SWT.NONE, SWT.LEFT, 200, new RepositoryLayerColumnLabelProvider()); addTableViewerColum(tableViewer, WizardMessages .getString("ImageTagSelectionPage.column.localcopy"), //$NON-NLS-1$ SWT.NONE, SWT.LEFT, 75, new ImagePulledColumnLabelProvider()); tableViewer.setContentProvider(new ObservableListContentProvider()); // observe the viewer content final IObservableList<?> observableSearchResultModel = BeanProperties .list(ImageSearchModel.class, ImageSearchModel.IMAGE_TAG_SEARCH_RESULT) .observe(model); tableViewer.setInput(observableSearchResultModel); // observe the viewer selection ctx.bindValue(ViewerProperties.singleSelection().observe(tableViewer), BeanProperties.value(ImageSearchModel.SELECTED_IMAGE_TAG) .observe(model)); setControl(container); } private TableViewerColumn addTableViewerColum(final TableViewer tableViewer, final String title, final int style, final int alignment, final int width, final CellLabelProvider columnLabelProvider) { final TableViewerColumn viewerColumn = new TableViewerColumn( tableViewer, style); final TableColumn column = viewerColumn.getColumn(); if (title != null) { column.setText(title); } column.setAlignment(alignment); column.setWidth(width); viewerColumn.setLabelProvider(columnLabelProvider); return viewerColumn; } static class RepositoryTagColumnLabelProvider extends ColumnLabelProvider { @Override public String getText(final Object element) { if (element instanceof DockerImageTagSearchResult) { return ((DockerImageTagSearchResult) element).getName(); } return super.getText(element); } } static class RepositoryLayerColumnLabelProvider extends ColumnLabelProvider { @Override public String getText(final Object element) { if (element instanceof DockerImageTagSearchResult) { final String layer = ((DockerImageTagSearchResult) element).getLayer(); if (layer == null || layer.equals(RepositoryTagV2.UNKNOWN_LAYER)) { return ""; //$NON-NLS-1$ } return layer; } return super.getText(element); } } static class ImagePulledColumnLabelProvider extends IconColumnLabelProvider { @Override boolean doPaint(final Object element) { return element instanceof DockerImageTagSearchResult && ((DockerImageTagSearchResult) element).isResolved(); } } }