org.cloudfoundry.ide.eclipse.server.ui.internal.wizards.CloudFoundryServiceWizardPage1.java Source code

Java tutorial

Introduction

Here is the source code for org.cloudfoundry.ide.eclipse.server.ui.internal.wizards.CloudFoundryServiceWizardPage1.java

Source

/*******************************************************************************
 * Copyright (c) 2014 Pivotal Software Inc and IBM Corporation. 
 * 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Apache License, 
 * Version 2.0 (the "License"); you may not use this file except in compliance 
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *  
 * Contributors:
 *     Pivotal Software, Inc. - initial API and implementation
 *     IBM Corporation - Additions to services wizard
 *******************************************************************************/
package org.cloudfoundry.ide.eclipse.server.ui.internal.wizards;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.cloudfoundry.client.lib.domain.CloudServiceOffering;
import org.cloudfoundry.client.lib.domain.CloudServicePlan;
import org.cloudfoundry.ide.eclipse.server.core.internal.CloudFoundryServer;
import org.cloudfoundry.ide.eclipse.server.core.internal.client.LocalCloudService;
import org.cloudfoundry.ide.eclipse.server.ui.ICloudFoundryServiceWizardIconProvider;
import org.cloudfoundry.ide.eclipse.server.ui.internal.CloudFoundryImages;
import org.cloudfoundry.ide.eclipse.server.ui.internal.Messages;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseWheelListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.forms.widgets.Hyperlink;
import org.eclipse.ui.statushandlers.StatusManager;
import org.eclipse.wst.server.core.IRuntime;

public class CloudFoundryServiceWizardPage1 extends WizardPage {
    protected static final String PAGE_NAME = CloudFoundryServiceWizardPage1.class.getName();

    protected ScrolledComposite scrollComp;

    protected String filter;

    protected Font boldFont;

    protected Color selEdgeColor;

    protected Color selFillColor;

    protected Text nameText;

    protected boolean updating = false;

    private List<CloudServiceOffering> serviceOfferings;

    private List<CFServiceWizUI> allServicesList = new ArrayList<CFServiceWizUI>();

    private List<CFServiceWizUI> selectedServicesList = new ArrayList<CFServiceWizUI>();

    CFWizServicePage1Validation servicePageValidation;

    CloudFoundryServer cloudServer;

    protected Group serviceInfoComposite;

    public LocalCloudService getService() {

        List<CFServiceWizUI> selectedServices = getSelectedList();
        if (selectedServices != null && selectedServices.size() == 1) {
            return selectedServices.get(0).convertToLocalCloudService();
        }

        return null;
    }

    public static final String INSTALL_SERVICE_TEXT = Messages.CloudFoundryServiceWizardPage1_TEXT_SELECT_SERVICE;

    public static final String DEFAULT_FILTER_TEXT = Messages.CloudFoundryServiceWizardPage1_TEXT_TYPE_FILTER;

    public static final String REMOVE_SERVICE_TEXT = Messages.COMMONTXT_REMOVE;

    public static final String ADD_SERVICE_TEXT = Messages.CloudFoundryServiceWizardPage1_TEXT_SELECT;

    public static final String DESCRIPTION_FINAL = Messages.CloudFoundryServiceWizardPage1_TEXT_FINISH;

    public static final String FILTER_TEXT = Messages.CloudFoundryServiceWizardPage1_TEXT_FILTER;

    private Composite layoutList;

    private ServiceListTraverseListener traverseListener;

    private Text filterText;

    private Text serviceNameText;

    /** Optional field -- used to provide icons in service wizard if available */
    CFServiceWizardDynamicIconLoader loader;

    private FontMetrics fontMetrics;

    public CloudFoundryServiceWizardPage1(CloudFoundryServer cloudServer) {
        super(PAGE_NAME);
        this.cloudServer = cloudServer;
        setTitle(Messages.CloudFoundryServiceWizardPage1_TTILE_SERVICE_CONFIG);
        setDescription(DESCRIPTION_FINAL);
        ImageDescriptor banner = CloudFoundryImages
                .getWizardBanner(cloudServer.getServer().getServerType().getId());
        if (banner != null) {
            setImageDescriptor(banner);
        }
        this.cloudServer = cloudServer;

        // Is there an icon provide for the service, if so, use it
        IRuntime runtime = cloudServer.getServer().getRuntime();
        String runtimeTypeId = runtime.getRuntimeType().getId();

        ICloudFoundryServiceWizardIconProvider provider = CloudFoundryServiceWizardIconProviderRegistry
                .getInstance().getIconProvider(runtimeTypeId);
        if (provider != null) {
            loader = new CFServiceWizardDynamicIconLoader(provider, this.cloudServer);
            loader.start();
        }

        servicePageValidation = new CFWizServicePage1Validation(this);
        setPageComplete(false);

        traverseListener = new ServiceListTraverseListener();
    }

    private static void sortServicePlans(List<CloudServiceOffering> configurations) {

        for (CloudServiceOffering offering : configurations) {
            Collections.sort(offering.getCloudServicePlans(), new Comparator<CloudServicePlan>() {
                public int compare(CloudServicePlan o1, CloudServicePlan o2) {
                    return o1.getName().compareTo(o2.getName());
                }
            });
        }
    }

    private boolean updateConfiguration() {
        try {
            getContainer().run(true, false, new IRunnableWithProgress() {
                public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
                    try {
                        serviceOfferings = cloudServer.getBehaviour().getServiceOfferings(monitor);

                        Collections.sort(serviceOfferings, new Comparator<CloudServiceOffering>() {
                            public int compare(CloudServiceOffering o1, CloudServiceOffering o2) {
                                return o1.getDescription().compareTo(o2.getDescription());
                            }
                        });
                        // Pivotal Tracker [77602464] - Leaving plans in order
                        // received by server keeps them in pricing order from
                        // low to high
                        // sortServicePlans(serviceOfferings);

                    } catch (CoreException e) {
                        throw new InvocationTargetException(e);
                    } catch (OperationCanceledException e) {
                        throw new InterruptedException();
                    } finally {
                        monitor.done();
                    }
                }
            });

            if (serviceOfferings != null) {
                for (CloudServiceOffering o : serviceOfferings) {
                    if (!allServicesList.contains(new CFServiceWizUI(o))) {
                        allServicesList.add(new CFServiceWizUI(o));
                    }
                }
            }

            return true;
        } catch (InvocationTargetException e) {
            IStatus status = cloudServer.error(NLS.bind(
                    Messages.CloudFoundryServiceWizardPage1_ERROR_CONFIG_RETRIVE, e.getCause().getMessage()), e);
            StatusManager.getManager().handle(status, StatusManager.LOG);
            setMessage(status.getMessage(), IMessageProvider.ERROR);
        } catch (InterruptedException e) {
            // ignore
        }

        return false;
    }

    Composite topComp;

    public void createControl(Composite parent) {
        Composite comp = new Composite(parent, SWT.NONE);

        topComp = comp;

        GridLayout layout = new GridLayout();
        layout.horizontalSpacing = convertHorizontalDLUsToPixels(comp, 4);
        layout.verticalSpacing = convertVerticalDLUsToPixels(comp, 4);
        layout.numColumns = 3;
        comp.setLayout(layout);

        GridData data;

        Composite filterComp = new Composite(comp, SWT.NONE);
        layout = new GridLayout();
        layout.numColumns = 3;
        layout.marginHeight = 0;
        layout.marginWidth = 0;
        layout.horizontalSpacing = convertHorizontalDLUsToPixels(comp, 4);
        layout.verticalSpacing = convertHorizontalDLUsToPixels(comp, 4);
        filterComp.setLayout(layout);
        data = new GridData(SWT.FILL, SWT.BEGINNING, true, false);
        data.horizontalSpan = 3;
        data.horizontalIndent = 0;
        data.verticalIndent = 0;
        filterComp.setLayoutData(data);

        final Label filterLabel = new Label(filterComp, SWT.NONE);
        filterLabel.setText(FILTER_TEXT);
        data = new GridData(GridData.BEGINNING, GridData.CENTER, false, false);
        filterLabel.setLayoutData(data);

        filterText = new Text(filterComp, SWT.SEARCH);
        filterText.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, true, false, 2, 1));
        filterText.setText(DEFAULT_FILTER_TEXT);

        scrollComp = new ScrolledComposite(comp, SWT.BORDER | SWT.V_SCROLL);
        data = new GridData(GridData.FILL, GridData.FILL, true, true, 3, 1);
        data.heightHint = 300;
        scrollComp.setLayoutData(data);
        scrollComp.setAlwaysShowScrollBars(true);
        scrollComp.setBackground(scrollComp.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
        int lineHeight = filterText.computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
        scrollComp.getVerticalBar().setPageIncrement(lineHeight * 3);
        scrollComp.getVerticalBar().setIncrement(lineHeight);

        scrollComp.addControlListener(new ControlAdapter() {
            public void controlResized(ControlEvent event) {
                Control c = scrollComp.getContent();
                if (c == null) {
                    return;
                }
                Rectangle r = scrollComp.getClientArea();

                r.height = c.computeSize(r.width, SWT.DEFAULT).y;

                c.setBounds(r);
            }
        });

        layoutList = createLayoutList(scrollComp);

        filterText.addModifyListener(new ModifyListener() {

            public void modifyText(ModifyEvent event) {
                if (updating) {
                    return;
                }

                String text = filterText.getText();
                if (DEFAULT_FILTER_TEXT.equals(text)) {
                    filter = null;
                } else {
                    filter = filterText.getText();
                }
                updateServicesTable();
            }
        });

        filterText.addFocusListener(new FocusListener() {

            public void focusLost(FocusEvent e) {
                if (filterText.getText().isEmpty()) {
                    updating = true;
                    filterText.setText(DEFAULT_FILTER_TEXT);
                    updating = false;
                }
            }

            public void focusGained(FocusEvent e) {
                if (DEFAULT_FILTER_TEXT.equals(filterText.getText())) {
                    updating = true;
                    filterText.setText(""); //$NON-NLS-1$
                    updating = false;
                }
            }
        });

        filterText.addTraverseListener(traverseListener);

        serviceInfoComposite = new Group(comp, SWT.NONE);
        serviceInfoComposite.setText(Messages.CloudFoundryServiceWizardPage1_TEXT_SET_NAME_PLAN);
        data = new GridData(GridData.FILL, GridData.FILL, true, false, 3, 1);
        data.verticalIndent = 5;
        serviceInfoComposite.setLayoutData(data);
        layout = new GridLayout();
        layout.numColumns = 2;
        layout.marginHeight = 5;
        layout.marginWidth = 5;
        serviceInfoComposite.setLayout(layout);

        serviceInfoComposite.setVisible(false);

        servicePageValidation.updatePageState();
        Dialog.applyDialogFont(comp);
        setControl(comp);

        MouseWheelListener mwl = new MouseWheelListener() {

            public void mouseScrolled(MouseEvent event) {
                scrollComp.setFocus();
            }
        };

        filterText.addMouseWheelListener(mwl);
        // nameText.addMouseWheelListener(mwl);

    }

    private Composite createLayoutList(Composite parent) {

        Composite result = new Composite(parent, SWT.NONE);

        result.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND));

        Display display = result.getDisplay();
        Font font = result.getFont();

        if (boldFont == null) {
            FontData[] fontData = font.getFontData();
            fontData[0].setStyle(SWT.BOLD);
            fontData[0].setHeight(fontData[0].getHeight());
            boldFont = new Font(display, fontData);

            Color c1 = display.getSystemColor(SWT.COLOR_LIST_SELECTION);
            Color c2 = display.getSystemColor(SWT.COLOR_LIST_BACKGROUND);
            selEdgeColor = new Color(display, (c1.getRed() + c2.getRed() * 3) / 4,
                    (c1.getGreen() + c2.getGreen() * 3) / 4, (c1.getBlue() + c2.getBlue() * 3) / 4);
            selFillColor = new Color(display, (c1.getRed() + c2.getRed() * 8) / 9,
                    (c1.getGreen() + c2.getGreen() * 8) / 9, (c1.getBlue() + c2.getBlue() * 8) / 9);

            result.addDisposeListener(new DisposeListener() {

                public void widgetDisposed(DisposeEvent event) {
                    if (boldFont != null && !boldFont.isDisposed()) {
                        boldFont.dispose();
                    }
                    if (selEdgeColor != null && !selEdgeColor.isDisposed()) {
                        selEdgeColor.dispose();
                    }
                    if (selFillColor != null && !selFillColor.isDisposed()) {
                        selFillColor.dispose();
                    }
                }
            });

        }

        result.addPaintListener(new PaintListener() {

            public void paintControl(PaintEvent event) {
                if (selectedServicesList != null && selectedServicesList.size() > 0
                        && selectedServicesList.get(0).getNameLabel().isVisible()) {

                    Rectangle r = selectedServicesList.get(0).getAppxLocation();
                    if (r != null) {
                        Rectangle r2 = new Rectangle(r.x, r.y, r.width, r.height);

                        r2.x -= 3;
                        r2.width += 6;

                        r2.y -= 3;
                        r2.height += 6;

                        event.gc.setBackground(selFillColor);
                        event.gc.fillRoundRectangle(r2.x, r2.y, r2.width, r2.height, 7, 7);

                        event.gc.setForeground(selEdgeColor);
                        event.gc.drawRoundRectangle(r2.x, r2.y, r2.width, r2.height, 7, 7);
                    }

                }

            }
        });

        Color color = result.getBackground();

        updateConfiguration();

        Control[] tabList = new Control[allServicesList.size()];

        for (int x = 0; x < allServicesList.size(); x++) {

            CFServiceWizUI service = allServicesList.get(x);

            final Label imgLabel = new Label(result, SWT.NONE);
            imgLabel.setBackground(color);
            imgLabel.setData(service);

            final Label nameLabel = new Label(result, SWT.WRAP);
            nameLabel.setData(service);
            nameLabel.setFont(boldFont);
            nameLabel.setBackground(color);

            nameLabel.setText(service.getName());

            tabList[x] = nameLabel;

            final Label descLabel = new Label(result, SWT.WRAP);
            descLabel.setData(service);
            descLabel.setBackground(color);
            String desc = service.getDescription();
            descLabel.setText(trimTextAtBound(desc, 200));

            service.setDescriptionLabel(descLabel);
            service.setImageLabel(imgLabel);
            service.setNameLabel(nameLabel);

            imgLabel.addMouseListener(new CFWizSelectItemListener(service));
            nameLabel.addMouseListener(new CFWizSelectItemListener(service));
            descLabel.addMouseListener(new CFWizSelectItemListener(service));

            descLabel.setToolTipText(wrapAndTrimTextAtBound(desc, 100));

        }

        result.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseDown(MouseEvent e) {

                for (final CFServiceWizUI service : allServicesList) {
                    if (service.getAppxLocation().contains(e.x, e.y)) {
                        selectItem(service);
                        break;
                    }

                }

            }

        });

        result.setTabList(tabList);

        for (Control c : tabList) {
            c.addTraverseListener(traverseListener);
        }

        return result;

    }

    @Override
    public void setVisible(boolean isVis) {
        super.setVisible(isVis);

        if (isVis) {
            // Set focus on scrolled composite whenever we become visible to
            // prevent missing mouse wheel events
            scrollComp.setFocus();

            updateConfiguration();
            updateServicesTable();

        }
    }

    protected void updateServicesTable() {

        Control content = scrollComp.getContent();
        if (content != null) {
            content.dispose();
        }

        if (allServicesList.isEmpty()) {
            Composite labelComp = new Composite(scrollComp, SWT.NONE);
            GridLayout layout = new GridLayout();
            layout.horizontalSpacing = 8;
            layout.verticalSpacing = 12;
            labelComp.setLayout(layout);
            labelComp.setBackground(scrollComp.getBackground());

            Label descriptionLabel = new Label(labelComp, SWT.NONE);
            descriptionLabel.setLayoutData(new GridData(GridData.FILL, GridData.BEGINNING, false, false));
            descriptionLabel.setBackground(scrollComp.getBackground());
            descriptionLabel.setText(Messages.CloudFoundryServiceWizardPage1_LABEL_NO_AVAIL_SERVICE);
            scrollComp.setContent(labelComp);
            labelComp.setSize(labelComp.computeSize(SWT.DEFAULT, SWT.DEFAULT));
            return;
        }

        final Composite comp = new Composite(scrollComp, SWT.NONE);
        comp.setBackground(scrollComp.getBackground());

        comp.setLayout(new CFServiceWizardLayout(this));

        comp.addPaintListener(new PaintListener() {

            public void paintControl(PaintEvent event) {
                List<CFServiceWizUI> selected = selectedServicesList;

                if (selected != null && selected.size() == 1) {

                    Rectangle r = selected.get(0).getAppxLocation();
                    if (r != null) {
                        Rectangle r2 = new Rectangle(r.x, r.y, r.width, r.height);

                        r2.x -= 3;
                        r2.width += 6;

                        r2.y -= 3;
                        r2.height += 6;

                        event.gc.setBackground(selFillColor);
                        event.gc.fillRoundRectangle(r2.x, r2.y, r2.width, r2.height, 7, 7);

                        event.gc.setForeground(selEdgeColor);
                        event.gc.drawRoundRectangle(r2.x, r2.y, r2.width, r2.height, 7, 7);
                    }

                }

            }
        });

        for (final CFServiceWizUI item : allServicesList) {
            Color c = null;

            boolean areWeInSelectedList = selectedServicesList.contains(item);

            if (areWeInSelectedList) {
                c = selFillColor;
            } else {
                c = comp.getBackground();
            }

            if (filter == null || filter.isEmpty() || matches(item, filter)) {
                final Label imgLabel = new Label(comp, SWT.NONE);
                imgLabel.setBackground(c);
                imgLabel.setData(item);
                if (loader != null) {
                    // // Uncomment out these lines for dynamic icon acquisition
                    // imgLabel.addPaintListener(new PaintListener() {
                    //
                    // public void paintControl(PaintEvent e) {
                    //
                    // if(imgLabel.getImage() == null) {
                    // loader.addIconToRetrieveList(item.getOffering(),
                    // imgLabel);
                    // }
                    // }
                    // });

                    loader.addIconToRetrieveList(item.getOffering(), imgLabel);
                }

                final Label nameLabel = new Label(comp, SWT.WRAP);
                nameLabel.setFont(boldFont);
                nameLabel.setBackground(c);
                nameLabel.setText(item.getName());

                nameLabel.setData(item);

                final Label descLabel = new Label(comp, SWT.WRAP);
                descLabel.setBackground(c);
                String desc = item.getDescription();
                if (desc == null) {
                    desc = ""; //$NON-NLS-1$
                }
                descLabel.setText(trimTextAtBound(desc, 200));

                descLabel.setToolTipText(wrapAndTrimTextAtBound(desc, 100));
                descLabel.setData(item);

                String hyperLink = null;
                if (hyperLink == null) {
                    hyperLink = item.getOffering().getUrl();
                }
                if (hyperLink == null) {
                    hyperLink = item.getOffering().getInfoUrl();
                }
                if (hyperLink == null) {
                    hyperLink = item.getOffering().getDocumentationUrl();
                }

                // final String hyperLinkFinal = hyperLink;

                final Hyperlink hyperLinkLabel = new Hyperlink(comp, SWT.WRAP);
                hyperLinkLabel.setBackground(c);
                hyperLinkLabel.setText(""); //$NON-NLS-1$
                hyperLinkLabel.setVisible(false);
                // if(hyperLink != null) {
                // hyperLinkLabel.setText("(link)");
                // hyperLinkLabel.setForeground(comp.getDisplay().getSystemColor(SWT.COLOR_DARK_BLUE));
                // hyperLinkLabel.setUnderlined(true);
                // hyperLinkLabel.addHyperlinkListener(new HyperlinkAdapter() {
                // public void linkActivated(HyperlinkEvent e) {
                // if(hyperLinkFinal != null) {
                // org.eclipse.swt.program.Program.launch(hyperLinkFinal);
                // }
                // }
                // });
                // }

                CFWizSelectItemListener itemListener = new CFWizSelectItemListener(item);

                imgLabel.addMouseListener(itemListener);
                nameLabel.addMouseListener(itemListener);
                descLabel.addMouseListener(itemListener);

                if (item != null) {
                    if (item.getImageLabel() != null)
                        item.getImageLabel().setVisible(true);
                    if (item.getNameLabel() != null)
                        item.getNameLabel().setVisible(true);
                    if (item.getDescriptionLabel() != null)
                        item.getDescriptionLabel().setVisible(true);
                }

            } else {
                // Filtered out items have no location
                item.setAppxLocation(null);

                if (item != null) {
                    if (item.getImageLabel() != null)
                        item.getImageLabel().setVisible(false);
                    if (item.getNameLabel() != null)
                        item.getNameLabel().setVisible(false);
                    if (item.getDescriptionLabel() != null)
                        item.getDescriptionLabel().setVisible(false);
                }
            }
        }

        if (layoutList != null)
            layoutList.layout();

        comp.addMouseListener(new MouseListener() {
            public void mouseDown(MouseEvent e) {

                for (final CFServiceWizUI item : allServicesList) {
                    // Skip filtered items that has null appxLocation.
                    if (item.getAppxLocation() != null && item.getAppxLocation().contains(e.x, e.y)) {
                        selectItem(item);
                        break;
                    }

                }

            }

            public void mouseDoubleClick(MouseEvent arg0) {
                // Ignore
            }

            public void mouseUp(MouseEvent arg0) {
                // Ignore
            }

        });

        scrollComp.setContent(comp);

        Rectangle r = scrollComp.getClientArea();
        r.height = comp.computeSize(r.width, SWT.DEFAULT).y;
        comp.setBounds(r);

        for (Control c : serviceInfoComposite.getChildren()) {
            c.dispose();
        }

        createServiceInfoComposite();

        serviceInfoComposite.layout();
        topComp.layout();

        servicePageValidation.updatePageState();

    }

    private void createServiceInfoComposite() {

        String name = ""; //$NON-NLS-1$

        final CFServiceWizUI selectedItemFinal;
        if (selectedServicesList.size() == 1) {
            selectedItemFinal = selectedServicesList.get(0);
            name = selectedItemFinal.getName();
        } else {
            selectedItemFinal = null;
        }

        serviceInfoComposite.setEnabled(selectedItemFinal != null);

        final Label serviceNameLabel = new Label(serviceInfoComposite, SWT.NONE);
        serviceNameLabel.setText(Messages.COMMONTXT_NAME_WITH_COLON);
        GridData data = new GridData(GridData.BEGINNING, GridData.CENTER, false, false);
        serviceNameLabel.setLayoutData(data);
        serviceNameLabel.setEnabled(selectedItemFinal != null);

        serviceNameText = new Text(serviceInfoComposite, SWT.SEARCH);
        serviceNameText.setLayoutData(new GridData(GridData.FILL, GridData.BEGINNING, true, false, 1, 1));
        serviceNameText.setText(utilRemoveSpaces(name));
        serviceNameText.setEnabled(selectedItemFinal != null);

        serviceNameText.addTraverseListener(traverseListener);

        if (selectedItemFinal != null) {
            selectedItemFinal.setUserDefinedName(utilRemoveSpaces(name));
        }
        serviceNameText.addModifyListener(new ModifyListener() {

            public void modifyText(ModifyEvent e) {

                String text = serviceNameText.getText();
                if (selectedItemFinal != null) {
                    selectedItemFinal.setUserDefinedName(text);
                }
                servicePageValidation.updatePageState();
            }

        });

        final Label servicePlanLabel = new Label(serviceInfoComposite, SWT.NONE);
        servicePlanLabel.setText(Messages.CloudFoundryServiceWizardPage1_LABEL_PLAN);
        servicePlanLabel.setEnabled(selectedItemFinal != null);
        data = new GridData(GridData.BEGINNING, GridData.CENTER, false, false);
        servicePlanLabel.setLayoutData(data);
        @SuppressWarnings("unchecked")
        final List<CloudServicePlan> plans = selectedItemFinal != null
                ? selectedItemFinal.getOffering().getCloudServicePlans()
                : Collections.EMPTY_LIST;

        String[] items = new String[plans.size()];
        for (int x = 0; x < items.length; x++) {
            items[x] = plans.get(x).getName();
        }

        final Combo typeCombo = new Combo(serviceInfoComposite, SWT.READ_ONLY | SWT.BORDER);
        typeCombo.setLayoutData(new GridData(GridData.FILL, GridData.BEGINNING, true, false, 1, 1));
        typeCombo.setItems(items);
        typeCombo.select(0);
        typeCombo.setEnabled(selectedItemFinal != null);

        typeCombo.addSelectionListener(new SelectionListener() {

            public void widgetSelected(SelectionEvent e) {
                if (selectedItemFinal != null) {
                    selectedItemFinal.setPlan(plans.get(typeCombo.getSelectionIndex()));
                }
            }

            public void widgetDefaultSelected(SelectionEvent e) {
                // Ignore.
            }
        });

        serviceInfoComposite.setVisible(true);

    }

    private static String utilRemoveSpaces(String str) {
        if (str == null) {
            return null;
        } else {
            return str.replace(" ", ""); //$NON-NLS-1$ //$NON-NLS-2$
        }
    }

    private void selectItem(CFServiceWizUI item) {

        if (selectedServicesList == null)
            return;

        if (selectedServicesList.contains(item)) {
            // Do nothing to disable deselect.
            return;
        } else {

            selectedServicesList.clear();

            selectedServicesList.add(item);

            // If the plan has not been set, use the first
            if (item != null && item.getPlan() == null) {
                List<CloudServicePlan> plans = item.getOffering().getCloudServicePlans();
                if (plans != null && plans.size() > 0) {
                    item.setPlan(plans.get(0));
                }
            }

        }

        servicePageValidation.updatePageState();

        Point p = scrollComp.getOrigin();

        updateServicesTable();

        scrollComp.setFocus();

        scrollComp.setOrigin(p);

        if (selectedServicesList.size() > 0) {
            selectServiceListItem(selectedServicesList.get(0));
        }

    }

    /** Update the selection rectangle in the composite for the service*/
    private void selectServiceListItem(CFServiceWizUI service) {
        if (service == null)
            return;

        if (selectedServicesList != null && selectedServicesList.size() > 0) {
            // Set the previously selected item back to normal 
            selectedServicesList.get(0).getDescriptionLabel().setBackground(layoutList.getBackground());
            selectedServicesList.get(0).getImageLabel().setBackground(layoutList.getBackground());
            selectedServicesList.get(0).getNameLabel().setBackground(layoutList.getBackground());
        }

        service.getDescriptionLabel().setBackground(selFillColor);
        service.getImageLabel().setBackground(selFillColor);
        service.getNameLabel().setBackground(selFillColor);

        service.getNameLabel().forceFocus();

        layoutList.redraw();

        if (service.getAppxLocation() != null) {

            Rectangle bounds = service.getAppxLocation(); //child.getBounds();
            Rectangle area = scrollComp.getClientArea();
            Point origin = scrollComp.getOrigin();

            // Our view is lower than the item
            if (origin.y > bounds.y) {
                origin.y = Math.max(0, bounds.y);
            }

            // Our view is above the item
            if (origin.y + area.height < bounds.y + bounds.height) {
                origin.y = Math.max(0, bounds.y + bounds.height - area.height);
            }

            scrollComp.setOrigin(origin);
        }

    }

    class CFWizSelectItemListener implements SelectionListener, MouseListener {

        CFServiceWizUI service;

        public CFWizSelectItemListener(CFServiceWizUI item2) {
            this.service = item2;
        }

        public void widgetSelected(SelectionEvent event2) {
            selectItem(service);

        }

        public void mouseDoubleClick(MouseEvent arg0) {
            // Ignore.
        }

        public void mouseDown(MouseEvent arg0) {
            selectItem(service);

        }

        public void mouseUp(MouseEvent arg0) {
            // Ignore.
        }

        public void widgetDefaultSelected(SelectionEvent arg0) {
            // Ignore.
        }

    }

    protected boolean matches(CFServiceWizUI item, String filter) {
        return item.getName().toLowerCase().contains(filter.toLowerCase())
                || item.getDescription().toLowerCase().contains(filter.toLowerCase());
    }

    public String getServiceName() {
        return nameText.getText();
    }

    public List<CFServiceWizUI> getSelectedList() {
        return selectedServicesList;
    }

    protected void initializeDialogUnits(Control testControl) {
        // Compute and store a font metric
        GC gc = new GC(testControl);
        gc.setFont(JFaceResources.getDialogFont());
        fontMetrics = gc.getFontMetrics();
        gc.dispose();
    }

    public int convertHorizontalDLUsToPixels(Composite comp, int x) {
        if (fontMetrics == null) {
            initializeDialogUnits(comp);
        }
        return Dialog.convertHorizontalDLUsToPixels(fontMetrics, x);
    }

    public int convertVerticalDLUsToPixels(Composite comp, int y) {
        if (fontMetrics == null) {
            initializeDialogUnits(comp);
        }
        return Dialog.convertVerticalDLUsToPixels(fontMetrics, y);
    }

    private static String trimTextAtBound(String str, int bound) {
        if (str.length() > bound) {
            /** Use the existing word wrap function to wrap at the spot we want. */
            String result = wrapAndTrimTextAtBound(str, bound);

            int index = result.indexOf("\n"); //$NON-NLS-1$
            if (index == -1) {
                return str;
            } else {
                return str.substring(0, index) + "..."; //$NON-NLS-1$
            }
        } else {
            return str;
        }

    }

    /** Simple text wrapper */
    private static String wrapAndTrimTextAtBound(String text, int bound) {
        StringBuilder result = new StringBuilder();
        String remainingString = text;

        // Last seen in the text
        int lastSpace = 0;

        for (int x = 0; x < remainingString.length(); x++) {
            if (remainingString.charAt(x) == ' ') {
                lastSpace = x;
            }
            if (x == bound) {
                // If there is no way to possible wrap it (e.g no spaces, or the
                // bound is too small) then don't bother
                if (lastSpace == 0) {
                    return text;
                }

                String nextLine = remainingString.substring(0, lastSpace);
                remainingString = remainingString.substring(lastSpace).trim();
                result.append(nextLine);
                result.append("\n"); //$NON-NLS-1$
                x = 0;
                lastSpace = 0;
            }

        }

        result.append(remainingString.trim());

        return result.toString();

    }

    protected boolean isIconProviderAvailable() {
        return loader != null;
    }

    @Override
    public void dispose() {
        super.dispose();
        if (loader != null) {
            this.loader.dispose();
        }
    }

    class ServiceListTraverseListener implements TraverseListener {
        @Override
        public void keyTraversed(TraverseEvent e) {

            if (((e.getSource() == filterText) && (e.detail == SWT.TRAVERSE_TAB_NEXT))
                    || ((e.getSource() == serviceNameText) && (e.detail == SWT.TRAVERSE_TAB_PREVIOUS))) {
                // Only one service can currently be selected right now

                int listSize = selectedServicesList.size();

                if (selectedServicesList != null && allServicesList != null) {
                    // An item was previously selected. Select that item again
                    if (listSize > 0) {
                        CFServiceWizUI item = selectedServicesList.get(0);
                        if (item != null && item.getNameLabel() != null && item.getNameLabel().isVisible()) {
                            selectServiceListItem(item);

                            // If doit is set to true, then the cursor will change focus to 
                            // the next text box. That behaviour is not good as it is difficult
                            // for accessibility to lose focus of the items in the list
                            e.doit = false;
                            return;
                        }
                    }

                    // Item is no longer selected (e.g. filter is used), so choose another item
                    CFServiceWizUI service = null;
                    int len = allServicesList.size();
                    for (int i = 0; i < len; i++) {
                        CFServiceWizUI currItem = allServicesList.get(i);
                        if (currItem != null && currItem.getNameLabel() != null) {
                            if (allServicesList.get(i).getNameLabel().isVisible()) {
                                service = allServicesList.get(i);
                                break;
                            }
                        }
                    }
                    // Service can be null if filter leads to 0 results
                    if (service != null) {
                        selectItem(service);
                    } else {
                        // If there are no items, the user is stuck in the keyboard only case if
                        // they tab next. Focus on the next text box instead
                        if (((e.getSource() == filterText) && (e.detail == SWT.TRAVERSE_TAB_NEXT))
                                && serviceNameText != null) {
                            serviceNameText.forceFocus();
                        } else if (((e.getSource() == serviceNameText) && (e.detail == SWT.TRAVERSE_TAB_PREVIOUS))
                                && filterText != null) {
                            filterText.forceFocus();
                        }
                    }
                }

                // If doit is set to true, then the cursor will change focus to 
                // the next text box. That behaviour is not good as it is difficult
                // for accessibility to lose focus of the items in the list
                e.doit = false;
            } else {
                if (e.getSource() instanceof Label && e.getSource() != null
                        && ((Label) e.getSource()).getData() instanceof CFServiceWizUI && allServicesList != null) {

                    // If you press tab while in the list, go outside of the list
                    if (e.detail == SWT.TRAVERSE_TAB_NEXT) {
                        if (serviceNameText != null)
                            serviceNameText.forceFocus();
                    }
                    if (e.detail == SWT.TRAVERSE_TAB_PREVIOUS) {
                        if (filterText != null)
                            filterText.forceFocus();
                    }
                    CFServiceWizUI selectedService = (CFServiceWizUI) ((Label) e.getSource()).getData();

                    CFServiceWizUI service = null;

                    int servicePos = -1;
                    for (int x = 0; x < allServicesList.size(); x++) {
                        if (selectedService == allServicesList.get(x)) {
                            servicePos = x;
                            break;
                        }
                    }

                    // If you press the arrow keys inside the list, go to the next visible item
                    if (servicePos != -1) {

                        if (e.detail == SWT.TRAVERSE_ARROW_NEXT) {
                            int len = allServicesList.size();
                            while (servicePos + 1 < len) {
                                servicePos++;
                                CFServiceWizUI currItem = allServicesList.get(servicePos);
                                if (currItem != null && currItem.getNameLabel() != null
                                        && currItem.getNameLabel().isVisible()) {
                                    service = allServicesList.get(servicePos);
                                    break;
                                }
                            }
                        } else if (e.detail == SWT.TRAVERSE_ARROW_PREVIOUS) {
                            while (servicePos > 0) {
                                servicePos--;
                                CFServiceWizUI currItem = allServicesList.get(servicePos);
                                if (currItem != null && currItem.getNameLabel() != null
                                        && currItem.getNameLabel().isVisible()) {
                                    service = allServicesList.get(servicePos);
                                    break;
                                }
                            }
                        }
                    }

                    if (service != null) {
                        selectItem(service);
                    }
                }
            }
        }
    }

}

class CFServiceWizUI {

    private CloudServiceOffering offering;

    private Button button;

    private Rectangle appxLocation;

    private String userDefinedName = ""; //$NON-NLS-1$

    private CloudServicePlan plan = null;

    Label nameLabel;
    Label descLabel;
    Label imageLabel;

    public CFServiceWizUI(CloudServiceOffering offering) {
        this.offering = offering;
    }

    public String getName() {
        return offering.getName();
    }

    public String getDescription() {
        return offering.getDescription();
    }

    public Button getButton() {
        return button;
    }

    public void setButton(Button button) {
        this.button = button;
    }

    public Rectangle getAppxLocation() {
        return appxLocation;
    }

    public void setAppxLocation(Rectangle appxLocation) {
        this.appxLocation = appxLocation;
    }

    public String getUserDefinedName() {
        return userDefinedName;
    }

    public void setUserDefinedName(String userDefinedName) {
        this.userDefinedName = userDefinedName;
    }

    public CloudServiceOffering getOffering() {
        return offering;
    }

    public CloudServicePlan getPlan() {
        return plan;
    }

    public void setPlan(CloudServicePlan plan) {
        this.plan = plan;
    }

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof CFServiceWizUI)) {
            return false;
        }
        CloudServiceOffering offering = ((CFServiceWizUI) o).getOffering();
        String name1 = "" + getOffering().getName(); //$NON-NLS-1$
        String name2 = "" + offering.getName(); //$NON-NLS-1$

        return name1.equals(name2);
    }

    public LocalCloudService convertToLocalCloudService() {
        final LocalCloudService localService = new LocalCloudService(""); //$NON-NLS-1$
        localService.setName(getUserDefinedName());
        localService.setVersion(getOffering().getVersion());
        localService.setLabel(getOffering().getLabel());
        localService.setProvider(getOffering().getProvider());
        localService.setPlan(getPlan().getName());
        return localService;
    }

    public Label getNameLabel() {
        return nameLabel;
    }

    public void setNameLabel(Label nameLabel) {
        this.nameLabel = nameLabel;
    }

    public Label getDescriptionLabel() {
        return descLabel;
    }

    public void setDescriptionLabel(Label descLabel) {
        this.descLabel = descLabel;
    }

    public Label getImageLabel() {
        return imageLabel;
    }

    public void setImageLabel(Label imageLabel) {
        this.imageLabel = imageLabel;
    }
}

class CFServiceWizardLayout extends Layout {
    private static final int BORDER = 5;

    private static int IMG_DEFAULT = 32;

    private static final int GAP = 3;

    private static final int V_SPACE = 3;

    protected int colWidth = -1;

    CloudFoundryServiceWizardPage1 parent;

    public CFServiceWizardLayout(CloudFoundryServiceWizardPage1 parent) {
        this.parent = parent;
    }

    @Override
    protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {
        int[] result = layoutImpl(composite, 0, flushCache, false);

        return new Point(result[0], result[1]);
    }

    @Override
    protected void layout(Composite composite, boolean flushCache) {
        int[] result = layoutImpl(composite, 0, flushCache, true);

        // Adjust the height to match the elments inside
        Rectangle r = composite.getBounds();
        r.height = result[1];
        composite.setBounds(r);
    }

    protected int[] layoutImpl(Composite composite, int wHint, boolean flushCache, boolean apply) {
        Rectangle r = composite.getClientArea();
        int areaW = r.width > 0 ? r.width : wHint;
        int i = 0;
        int x = r.x + BORDER;
        int y = r.y + BORDER;
        int w = 0;
        if (areaW == 0) {
            w = composite.getParent().getBounds().width - composite.getParent().getBorderWidth() * 2;
        } else {
            w = areaW - BORDER * 2;
        }
        w = Math.max(400, w);

        int bottomMostY = -1;
        int rightMostX = -1;

        boolean iconsAvailable = parent.isIconProviderAvailable();

        int IMG;
        if (iconsAvailable) {
            IMG = IMG_DEFAULT;
        } else {
            IMG = 0;
        }

        // children are: image, name, description, size
        Control[] children = composite.getChildren();

        final int initialX = x;

        while (i < children.length) {

            x = initialX;

            if (i > 0) {
                y += V_SPACE;
            }

            Label localIconLabel = (Label) children[i];

            Label localNameLabel = (Label) children[i + 1];

            CFServiceWizUI product = (CFServiceWizUI) localNameLabel.getData();

            // image
            if (apply) {
                localIconLabel.setBounds(x, y, IMG, IMG);
            }

            if (iconsAvailable) {
                x += 3;
            }

            // label

            Point p = localNameLabel.computeSize(w - IMG - GAP, SWT.DEFAULT, flushCache);
            if (apply) {
                localNameLabel.setBounds(x + IMG + GAP, y, p.x, p.y);
            }

            y += p.y + GAP;

            // hyperlink
            Hyperlink localLink = (Hyperlink) children[i + 3];
            p = localLink.computeSize(SWT.DEFAULT, SWT.DEFAULT, flushCache);
            if (apply) {
                localLink.setBounds(x + w - p.x - 2, y, p.x, p.y);
            }

            // description
            Label localDescr = (Label) children[i + 2];
            p = localDescr.computeSize(w - IMG - GAP * 2, SWT.DEFAULT, flushCache);
            if (apply) {
                localDescr.setBounds(x + IMG + GAP, y, p.x, p.y);
            }

            y = y + p.y;

            Rectangle imgLabelRect = localIconLabel.getBounds();
            int topLeftX = imgLabelRect.x;
            int topLeftY = imgLabelRect.y;

            Rectangle descLabelRect = localDescr.getBounds();
            int botRightX = descLabelRect.x + descLabelRect.width;
            int botRightY = descLabelRect.y + descLabelRect.height;

            bottomMostY = botRightY;
            rightMostX = botRightX;

            Rectangle rowPos = new Rectangle(topLeftX, topLeftY, botRightX - topLeftX, botRightY - topLeftY);
            if (apply) {
                product.setAppxLocation(rowPos);
            }

            y += BORDER;
            i += 4;

        }

        bottomMostY += BORDER;

        return new int[] { rightMostX, bottomMostY };
    }
}

class CFWizServicePage1Validation {

    private static Pattern VALID_CHARS = Pattern.compile("[A-Za-z\\$_0-9\\-]+"); //$NON-NLS-1$

    CloudFoundryServiceWizardPage1 wizardPage;

    public CFWizServicePage1Validation(CloudFoundryServiceWizardPage1 wizardPage) {
        this.wizardPage = wizardPage;
    }

    public void updatePageState() {

        boolean descriptionUpdated = false;

        if (!descriptionUpdated) {

            List<CFServiceWizUI> list = wizardPage.getSelectedList();
            if (list == null || list.size() == 0) {
                wizardPage.setDescription(Messages.CloudFoundryServiceWizardPage1_TEXT_SELECT_SERVICE_LIST);
                wizardPage.setErrorMessage(null);
                descriptionUpdated = true;
            }

        }

        if (!descriptionUpdated) {
            List<CFServiceWizUI> list = wizardPage.getSelectedList();
            if (list != null && list.size() == 1) {
                CFServiceWizUI service = list.get(0);

                String userDefinedName = service.getUserDefinedName();

                if (service != null && userDefinedName != null) {

                    if (userDefinedName.trim().length() == 0) {
                        wizardPage.setDescription(Messages.CloudFoundryServiceWizardPage1_TEXT_SET_SERVICE_NAME);
                        wizardPage.setErrorMessage(null);
                        descriptionUpdated = true;
                    }

                    Matcher matcher = VALID_CHARS.matcher(userDefinedName);
                    if (!descriptionUpdated && !matcher.matches()) {
                        wizardPage.setErrorMessage(Messages.CloudFoundryServiceWizardPage1_ERROR_INVALID_CHAR);
                        wizardPage.setDescription(null);
                        descriptionUpdated = true;
                    }

                }

            }

        }

        if (!descriptionUpdated) {
            wizardPage.setErrorMessage(null);
            wizardPage.setDescription(CloudFoundryServiceWizardPage1.DESCRIPTION_FINAL);
            wizardPage.setPageComplete(true);
        } else {
            wizardPage.setPageComplete(false);
        }

    }

}

class CFServiceWizardDynamicIconLoader extends Thread {

    private final Object lock = new Object();

    // List synchronized on lock
    private List<ServiceWizardMapEntry> iconsToRetrieve = new ArrayList<ServiceWizardMapEntry>();

    private CloudFoundryServer server;

    private ICloudFoundryServiceWizardIconProvider iconProvider;

    private boolean isRunning = true;

    // Synchronize on imageMap when accessing it
    private Map<String /* service id + provider */, Image> imageMap = new HashMap<String /*
                                                                                         * offering
                                                                                         * id
                                                                                         */, Image>();

    public CFServiceWizardDynamicIconLoader(ICloudFoundryServiceWizardIconProvider iconProvider,
            CloudFoundryServer server) {
        super(CFServiceWizardDynamicIconLoader.class.getName());
        setDaemon(true);
        this.iconProvider = iconProvider;
        this.server = server;
    }

    /**
     * Add icon to the front of the list, for icons the user is currently
     * viewing
     */
    public void addIconToFrontOfRetrieveList(CloudServiceOffering offering, Label imageLabel) {
        synchronized (lock) {

            // Add to front (this may create a duplicate; dupes are checked in
            // IconRetrieveRunnable)
            ServiceWizardMapEntry me = new ServiceWizardMapEntry(offering, imageLabel);
            iconsToRetrieve.add(0, me);
            lock.notifyAll();
        }
    }

    /** Add icon to end of list */
    public void addIconToRetrieveList(CloudServiceOffering offering, Label imageLabel) {
        ServiceWizardMapEntry me = new ServiceWizardMapEntry(offering, imageLabel);
        synchronized (lock) {
            iconsToRetrieve.add(me);
            lock.notifyAll();
        }
    }

    public void run() {
        // Have up to 5 URL requests running at a time
        ExecutorService es = Executors.newFixedThreadPool(5);

        while (isRunning) {

            try {

                List<ServiceWizardMapEntry> localIconsToRetrieve = null;

                synchronized (lock) {
                    lock.wait(1000);

                    if (isRunning && iconsToRetrieve.size() > 0) {

                        localIconsToRetrieve = new ArrayList<ServiceWizardMapEntry>();
                        localIconsToRetrieve.addAll(iconsToRetrieve);
                        iconsToRetrieve.clear();
                    }

                }

                if (isRunning && localIconsToRetrieve != null) {
                    // Process icon requests outside the lock

                    for (ServiceWizardMapEntry e : localIconsToRetrieve) {
                        IconRetrieveRunnable r = new IconRetrieveRunnable(e);
                        es.execute(r);
                    }

                    localIconsToRetrieve.clear();
                }

            } catch (InterruptedException e) {
                isRunning = false;
            }

        }

        es.shutdownNow();

        try {
            // Wait at most 10 seconds for the remaining tasks to finish
            es.awaitTermination(10, TimeUnit.SECONDS);
        } catch (InterruptedException e1) {
            // ignore.
        }

        synchronized (imageMap) {
            // Dispose of old images
            Set<Entry<String, Image>> s = imageMap.entrySet();
            for (Entry<String, Image> e : s) {
                if (e.getValue().isDisposed()) {
                    e.getValue().dispose();
                }
            }
        }

    }

    public void dispose() {
        synchronized (lock) {
            isRunning = false;
        }
    }

    private class IconRetrieveRunnable implements Runnable {

        ServiceWizardMapEntry entry;

        public IconRetrieveRunnable(ServiceWizardMapEntry entry) {
            this.entry = entry;
        }

        public void run() {

            CloudServiceOffering cso = entry.getOffering();
            final String mapId = "" + cso.getName() + "-" + cso.getProvider(); //$NON-NLS-1$ //$NON-NLS-2$

            Image result = null;

            // Check the cache for the image
            synchronized (imageMap) {
                Image i = imageMap.get(mapId);
                if (i != null) {
                    result = i;
                }
            }

            // Grab the image from the provider, if needed
            if (result == null) {
                ImageDescriptor imageDesc = iconProvider.getServiceIcon(entry.getOffering(), server);

                if (imageDesc != null) {
                    Image img = imageDesc.createImage();
                    final Image resizeImg = resizeImage(img, 32, 32);
                    result = resizeImg;
                    synchronized (imageMap) {
                        imageMap.put(mapId, resizeImg);
                    }
                }
            }

            final Image labelImage = result;

            if (labelImage == null)
                return;

            // Replace the image label
            Display.getDefault().asyncExec(new Runnable() {
                public void run() {
                    Label l = entry.getImageLabel();

                    if (!l.isDisposed()) {
                        l.setImage(labelImage);
                    }
                }
            });

        }

        private Image resizeImage(Image oldImage, int newWidth, int newHeight) {
            Image newImage = new Image(Display.getDefault(), newWidth, newHeight);
            GC gc = new GC(newImage);
            gc.setAntialias(SWT.ON);
            gc.setInterpolation(SWT.HIGH);
            gc.drawImage(oldImage, 0, 0, oldImage.getBounds().width, oldImage.getBounds().height, 0, 0, newWidth,
                    newHeight);
            gc.dispose();
            oldImage.dispose();
            return newImage;
        }

    }

    private static class ServiceWizardMapEntry {
        private CloudServiceOffering offering;

        private Label imageLabel;

        public ServiceWizardMapEntry(CloudServiceOffering offering, Label imageLabel) {
            super();
            this.offering = offering;
            this.imageLabel = imageLabel;
        }

        public CloudServiceOffering getOffering() {
            return offering;
        }

        public Label getImageLabel() {
            return imageLabel;
        }

    }

}