org.springframework.ide.eclipse.wizard.template.TemplateSelectionPart.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.ide.eclipse.wizard.template.TemplateSelectionPart.java

Source

/*******************************************************************************
 *  Copyright (c) 2012, 2013 GoPivotal, Inc.
 *  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:
 *      GoPivotal, Inc. - initial API and implementation
 *******************************************************************************/
package org.springframework.ide.eclipse.wizard.template;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.xml.parsers.DocumentBuilder;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.preference.PreferenceDialog;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.DecorationOverlayIcon;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.jface.wizard.IWizard;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
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.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.eclipse.ui.forms.events.HyperlinkAdapter;
import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.forms.widgets.Hyperlink;
import org.springframework.ide.eclipse.wizard.WizardImages;
import org.springframework.ide.eclipse.wizard.WizardPlugin;
import org.springframework.ide.eclipse.wizard.template.infrastructure.ITemplateElement;
import org.springframework.ide.eclipse.wizard.template.infrastructure.SimpleProjectContentManager;
import org.springframework.ide.eclipse.wizard.template.infrastructure.Template;
import org.springframework.ide.eclipse.wizard.template.infrastructure.TemplateCategory;
import org.springframework.ide.eclipse.wizard.template.util.TemplatesPreferencePage;
import org.springframework.ide.eclipse.wizard.template.util.TemplatesPreferencesModel;
import org.springsource.ide.eclipse.commons.content.core.ContentItem;
import org.springsource.ide.eclipse.commons.content.core.ContentManager;
import org.springsource.ide.eclipse.commons.content.core.ContentPlugin;
import org.springsource.ide.eclipse.commons.content.core.util.ContentUtil;
import org.springsource.ide.eclipse.commons.content.core.util.Descriptor;
import org.springsource.ide.eclipse.commons.content.core.util.IContentConstants;
import org.springsource.ide.eclipse.commons.ui.StsUiImages;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * Part that displays templates available when creating a project. Templates are
 * displayed in the part viewer based on metadata, but the actual contents of
 * the template are not download unless explicitly requested through the
 * download API. Selecting templates in the part viewer does not automatically
 * download the contents. This template selection area only performs validations
 * based on whether a template is selected or not.
 * @author Terry Denney
 * @author Leo Dos Santos
 * @author Christian Dupuis
 * @author Kaitlin Duck Sherwood
 * @author Nieraj Singh
 */
public class TemplateSelectionPart extends WizardPageArea {

    public static final String SIMPLE_PROJECTS_CATEGORY = "Simple Projects";

    private final List<Template> templates;

    private final IWizard wizard;

    private Label descriptionLabel;

    private StyledText descriptionText;

    private TreeViewer treeViewer;

    private Label legendImage;

    private Label legendText;

    private PropertyChangeListener contentManagerListener;

    private Button refreshButton;

    private final NewSpringProjectWizardModel model;

    public TemplateSelectionPart(IWizard wizard, NewSpringProjectWizardModel model,
            IWizardPageStatusHandler statusHandler) {
        super(statusHandler);
        this.wizard = wizard;
        this.model = model;
        templates = new ArrayList<Template>();

    }

    @Override
    public Control createArea(Composite parent) {

        initialiseTemplatesFromContentManager();

        Composite container = new Composite(parent, SWT.NONE);
        GridLayoutFactory.fillDefaults().margins(0, 0).spacing(0, 0).applyTo(container);
        container.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

        Label label = new Label(container, SWT.NONE);
        label.setText("Templates:"); //$NON-NLS-1$
        label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));

        Tree tree = new Tree(container, SWT.FULL_SELECTION | SWT.BORDER);
        tree.setLinesVisible(false);
        tree.setHeaderVisible(false);
        tree.setEnabled(true);

        GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
        data.heightHint = 170;
        tree.setLayoutData(data);

        treeViewer = new TreeViewer(tree);

        treeViewer.setLabelProvider(new ILabelProvider() {

            public void removeListener(ILabelProviderListener listener) {
            }

            public boolean isLabelProperty(Object element, String property) {
                return false;
            }

            public void dispose() {
            }

            public void addListener(ILabelProviderListener listener) {
            }

            public String getText(Object element) {
                if (element instanceof ITemplateElement) {
                    return ((ITemplateElement) element).getName();
                }
                return null;
            }

            public Image getImage(Object element) {
                if (element instanceof Template) {
                    Template template = (Template) element;
                    Image templateImage = WizardImages.getImage(WizardImages.TEMPLATE_ICON);

                    // Simple Project templates are bundled in the plugin,
                    // therefore do not require download icon, since they are
                    // not downloaded
                    if (template instanceof SimpleProject
                            || ((template.getItem().isLocal() || TemplateUtils.hasBeenDownloaded(template))
                                    && !template.getItem().isNewerVersionAvailable())) {
                        return templateImage;
                    }

                    return WizardImages.getImage(new DecorationOverlayIcon(templateImage,
                            new ImageDescriptor[] { StsUiImages.DOWNLOAD_OVERLAY, null, null, null, null }));
                }

                if (element instanceof TemplateCategory) {
                    return WizardImages.getImage(WizardImages.TEMPLATE_CATEGORY_ICON);
                }
                return null;
            }
        });

        treeViewer.setSorter(new ViewerSorter() {
            @Override
            public int compare(Viewer viewer, Object e1, Object e2) {
                if (e1 instanceof TemplateCategory && e2 instanceof Template) {
                    return -1;
                }
                if (e1 instanceof Template && e2 instanceof TemplateCategory) {
                    return 1;
                }

                if (e1 instanceof ITemplateElement && e2 instanceof ITemplateElement) {
                    ITemplateElement t1 = (ITemplateElement) e1;
                    ITemplateElement t2 = (ITemplateElement) e2;
                    // Special Case, Simple Projects category is placed above
                    // others
                    if (t1.getName().equals(SIMPLE_PROJECTS_CATEGORY)) {
                        return -1;
                    } else if (t2.getName().equals(SIMPLE_PROJECTS_CATEGORY)) {
                        return 1;
                    } else {
                        return t1.getName().compareTo(t2.getName());
                    }
                }
                return super.compare(viewer, e1, e2);
            }
        });

        treeViewer.setContentProvider(new TemplateContentProvider());

        treeViewer.setInput(templates);

        Composite legendContainer = new Composite(container, SWT.NONE);
        legendContainer.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
        GridLayout headerLayout = new GridLayout(3, false);
        headerLayout.marginWidth = 0;
        headerLayout.marginHeight = 0;
        legendContainer.setLayout(headerLayout);

        Composite legendComposite = new Composite(legendContainer, SWT.NONE);
        GridLayout legendLayout = new GridLayout(2, false);
        legendLayout.verticalSpacing = 0;
        legendLayout.marginHeight = 0;
        legendLayout.marginBottom = 0;

        int legendControlVerticalIndent = 5;

        legendComposite.setLayout(legendLayout);
        legendComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));

        legendImage = new Label(legendComposite, SWT.NONE);

        GridDataFactory.fillDefaults().grab(false, false).indent(0, legendControlVerticalIndent)
                .applyTo(legendImage);

        legendImage.setImage(WizardImages.getImage(StsUiImages.DOWNLOAD_OVERLAY));
        legendImage.setToolTipText("Templates with this icon will be downloaded after clicking the 'Next' button.");

        legendText = new Label(legendComposite, SWT.NONE);
        legendText.setText("requires downloading");

        GridDataFactory.fillDefaults().grab(false, false).indent(0, legendControlVerticalIndent)
                .applyTo(legendText);

        Hyperlink hyperlink = new Hyperlink(legendContainer, SWT.WRAP);
        GridDataFactory.fillDefaults().grab(false, false).indent(0, legendControlVerticalIndent).applyTo(hyperlink);
        hyperlink.setText(NLS.bind("Configure templates...", null));
        Color blue = new Color(null, 0, 0, 255);
        hyperlink.setForeground(blue);
        hyperlink.setUnderlined(true);
        blue.dispose();
        hyperlink.addHyperlinkListener(new HyperlinkAdapter() {
            @Override
            public void linkActivated(HyperlinkEvent event) {
                PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(null,
                        TemplatesPreferencePage.EXAMPLE_PREFERENCES_PAGE_ID, null, null);
                refreshButton.setEnabled(false);

                dialog.open();
                downloadDescriptors();

                refreshButton.setEnabled(!isRefreshing());

            }
        });

        refreshButton = new Button(legendContainer, SWT.PUSH);
        refreshButton.setLayoutData(new GridData(SWT.RIGHT, SWT.FILL, false, false));
        refreshButton.setText("Refresh");
        refreshButton.setEnabled(!isRefreshing());

        // refreshButton.setImage(WizardImages.getImage(WizardImages.REFRESH_ICON));

        refreshButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {

                // under most circumstances, we don't want to download templates
                // if there has not been any change. However, when the user
                // presses Refresh, they really do want to see something happen.
                ContentPlugin.getDefault().getManager().setDirty();

                downloadDescriptors();
            }

        });

        refreshButton.setToolTipText(
                "Refresh the list of templates. Note that the current template selection will be cleared.");

        Composite descriptionComposite = new Composite(container, SWT.NONE);
        descriptionComposite.setLayout(new GridLayout());
        descriptionComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));

        descriptionLabel = new Label(descriptionComposite, SWT.NONE);
        descriptionLabel.setText("Description:"); //$NON-NLS-1$
        descriptionLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));

        descriptionText = new StyledText(descriptionComposite, SWT.WRAP | SWT.READ_ONLY | SWT.V_SCROLL);
        // descriptionText.setAlwaysShowScrollBars(false);
        Display display = Display.getCurrent();
        if (display != null) {
            descriptionText.setBackground(display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
        }

        GridData descriptionData = new GridData(SWT.FILL, SWT.FILL, true, true);
        descriptionData.widthHint = 200;
        descriptionData.heightHint = 80;
        descriptionText.setLayoutData(descriptionData);

        treeViewer.addSelectionChangedListener(new ISelectionChangedListener() {

            public void selectionChanged(SelectionChangedEvent event) {
                ISelection selection = treeViewer.getSelection();
                if (selection instanceof TreeSelection) {
                    Object element = ((TreeSelection) selection).getFirstElement();
                    if (element instanceof Template) {
                        setSeletectedTemplate((Template) element);
                    } else if (element instanceof TemplateCategory) {
                        // Note: ONLY clear the selected template if the
                        // selection is a template category.
                        // Do not clear the template if there is an empty
                        // selection in the tree, as there
                        // are times when the viewer
                        // will set an empty selection in the tree due to a
                        // refresh operation, and if
                        // a template was already selected prior to that refresh
                        // request, it should not be
                        // overwritten with null as other operations will
                        // require that the selected template not be changed
                        setSeletectedTemplate(null);
                    }
                }
            }

        });

        this.contentManagerListener = new PropertyChangeListener() {

            public void propertyChange(PropertyChangeEvent event) {

                ContentManager manager = event.getSource() instanceof ContentManager
                        ? (ContentManager) event.getSource()
                        : null;
                final boolean hasTemplateContentChanged = manager != null && manager.isDirty();
                if (hasTemplateContentChanged) {

                    clearTemplateSelection();

                    // Only reinitialise templates if the manager is marked as
                    // dirty, as template contents may have changed, therefore
                    // new templates
                    // need to be created in the tree viewer
                    initialiseTemplatesFromContentManager();

                }
                // switch to UI thread to refresh the UI controls
                PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() {
                    public void run() {
                        refreshPage(hasTemplateContentChanged);
                    }
                });

            }

        };

        ContentManager manager = ContentPlugin.getDefault().getManager();
        manager.addListener(contentManagerListener);

        // This does not automatically add the templates to the tree viewer
        // input.
        // Rather it downloads templates asynchronously, and the content manager
        // will then notify the tree viewer when the content is available and at
        // that time
        // refresh the tree input
        downloadDescriptors();

        expandCategory(SIMPLE_PROJECTS_CATEGORY);

        return container;

    }

    protected void clearTemplateSelection() {
        // Run synch in UI thread as it is most likely called from a non-worker
        // thread
        PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() {
            public void run() {
                // clear any selection in the tree viewer as well.
                if (treeViewer != null && !treeViewer.getTree().isDisposed()) {
                    treeViewer.setSelection(null);
                }
                setSeletectedTemplate(null);
            }
        });

    }

    protected void expandCategory(final String categoryName) {
        // Run in UI

        if (treeViewer == null || treeViewer.getTree().isDisposed()) {
            return;
        }
        TreeItem[] items = treeViewer.getTree().getItems();
        if (items != null) {
            for (TreeItem item : items) {
                Object itemObj = item.getData();
                if (itemObj instanceof TemplateCategory
                        && ((TemplateCategory) itemObj).getName().equals(categoryName)) {

                    // Note that calling expand on the tree item itself would
                    // not work, because at this stage
                    // the tree viewer content provider may not have yet
                    // contributed children to the category element
                    // and therefore the category tree item may not have
                    // corresponding tree item children to display.
                    // Instead, expand through the treeViewer rather than
                    // through the tree item, as the treeViewer expand request
                    // will trigger the content provider to create children for
                    // the category tree item.

                    treeViewer.expandToLevel(itemObj, 1);
                    break;
                }
            }
        }

    }

    /**
     * Refreshes list of descriptors, and clears any current template selection.
     * This is to avoid retaining template selections that may no longer be up
     * to date.
     */
    protected void downloadDescriptors() {

        // Must execute as an asynch in UI thread to avoid Invalid UI
        // thread
        // exceptions
        PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
            public void run() {
                final String[] error = new String[1];
                final Exception[] e = new Exception[1];
                try {
                    wizard.getContainer().run(true, true, new DownloadDescriptorJob());
                } catch (InvocationTargetException ie) {
                    e[0] = ie;
                    error[0] = ErrorUtils.getErrorMessage("Failed to download template descriptors", e[0]);
                } catch (InterruptedException inte) {
                    e[0] = inte;
                    error[0] = "Download of descriptors interrupted";
                }

                if (error[0] != null && e[0] != null) {
                    setError(error[0], e[0]);
                }
            }
        });

    }

    protected void setSeletectedTemplate(Template template) {
        model.selectedTemplate.setValue(template);
        if (template != null) {
            setDescription(template);
        }
        notifyStatusChange(validateArea());
    }

    @Override
    protected IStatus validateArea() {
        Template template = model.selectedTemplate.getValue();
        IStatus status = Status.OK_STATUS;
        if (template != null) {

            String warning = getWarning(template);

            // Warnings apply to both Simple Projects and templates
            if (warning != null) {
                status = createStatus(warning, IStatus.WARNING);
            } else if (!(template instanceof SimpleProject)) {
                String message = "Click 'Next' to load the template contents.";
                status = createStatus(message, IStatus.INFO);
            }

            // For the purposes of selecting a template, if it is not null, the
            // area is complete.
            // Other sections in the wizard may provider additional validation
            // on the template, but it is not the responsibility of the template
            // selection
            // area to provide validation beyond just selecting a template
            setAreaComplete(true);

        } else {
            setAreaComplete(false);
            status = createStatus("Please select a template.", IStatus.INFO);
        }
        return status;
    }

    protected void setError(String error, Throwable t) {
        notifyStatusChange(new Status(IStatus.ERROR, WizardPlugin.PLUGIN_ID, error, t), false);
    }

    protected void setError(IStatus error) {
        notifyStatusChange(error, false);
    }

    /**
     * This initialises the templates directly from the content manager with
     * updated information. Any existing selections in the model are also
     * removed, as that template may no longer be valid.
     * 
     * Note that is is sometimes run in a worker thread, therefore any UI
     * callbacks must be performed as UI jobs.
     * 
     */
    private void initialiseTemplatesFromContentManager() {

        templates.clear();

        TemplatesPreferencesModel model = TemplatesPreferencesModel.getInstance();
        Collection<ContentItem> items = ContentPlugin.getDefault().getManager()
                .getItemsByKind(ContentManager.KIND_TEMPLATE);

        List<ContentItem> sortedItems = new ArrayList<ContentItem>();
        sortedItems.addAll(items);
        Collections.sort(sortedItems, new Comparator<ContentItem>() {
            public int compare(ContentItem o1, ContentItem o2) {
                Descriptor descriptor1 = o1.getRemoteDescriptor();
                if (descriptor1 == null) {
                    descriptor1 = o1.getLocalDescriptor();
                }

                Descriptor descriptor2 = o2.getRemoteDescriptor();
                if (descriptor2 == null) {
                    descriptor2 = o2.getLocalDescriptor();
                }
                return descriptor1.getVersion().compareTo(descriptor2.getVersion()) * -1;
            }
        });

        Set<String> templateIds = new HashSet<String>();

        for (ContentItem item : sortedItems) {
            String templateId = item.getId();
            if (!templateIds.contains(templateId)) {

                Template template = new Template(item, null);

                templates.add(template);
                templateIds.add(templateId);
            }
        }

        // Add the Simple Projects
        List<SimpleProject> simpleProjects = getSimpleProjects();
        templates.addAll(simpleProjects);

        if (model.shouldShowSelfHostedProjects()) {

            IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
            for (IProject project : projects) {
                IFile templateFile = project.getFile(IContentConstants.TEMPLATE_DATA_FILE_NAME);
                IFile wizardFile = project.getFile(IContentConstants.WIZARD_DATA_FILE_NAME);
                if (templateFile.exists() && wizardFile.exists()) {
                    File file = templateFile.getLocation().toFile();
                    try {
                        DocumentBuilder documentBuilder = ContentUtil.createDocumentBuilder();
                        Document document = documentBuilder.parse(file);
                        Element rootNode = document.getDocumentElement();
                        if (rootNode != null) {
                            NodeList children = rootNode.getChildNodes();
                            for (int i = 0; i < children.getLength(); i++) {
                                Node childNode = children.item(i);
                                if (childNode.getNodeType() == Node.ELEMENT_NODE) {
                                    if ("descriptor".equals(childNode.getNodeName())) {
                                        Descriptor descriptor = Descriptor.read(childNode);
                                        ContentItem item = new ContentItem(descriptor.getId(), project);
                                        item.setLocalDescriptor(descriptor);
                                        descriptor.setUrl(project.getName());
                                        ImageDescriptor icon = null;
                                        Template template = new Template(item, icon);
                                        templates.add(template);
                                    }
                                }
                            }
                        }
                    } catch (CoreException e) {
                        String message = NLS.bind("Error getting and parsing descriptors file in background {0}",
                                e.getMessage());
                        MessageDialog.openWarning(wizard.getContainer().getShell(), "Warning", message);
                    } catch (SAXException e) {
                        String message = NLS.bind("Error parsing tmp descriptors file at {0} in background.\n{1}",
                                file, e.getMessage());
                        MessageDialog.openWarning(wizard.getContainer().getShell(), "Warning", message);
                    } catch (IOException e) {
                        String message = NLS.bind("IO error on file at {0} opened in background.\n{1}", file,
                                e.getMessage());
                        MessageDialog.openWarning(wizard.getContainer().getShell(), "Warning", message);
                    }

                }
            }
        }
        Collections.sort(templates, new Comparator<Template>() {
            public int compare(Template t1, Template t2) {
                return t1.getName().compareTo(t2.getName());
            }
        });

    }

    /**
     * Returns non-null list of Simple Projects. If errors occur while resolving
     * simple projects, an empty list is returned, and error logged in the
     * container of the part
     */
    protected List<SimpleProject> getSimpleProjects() {

        final List<SimpleProject> projects = new ArrayList<SimpleProject>();
        PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() {
            public void run() {

                final String[] error = new String[1];
                final Exception[] exception = new Exception[1];

                try {
                    IRunnableWithProgress runnable = new IRunnableWithProgress() {

                        public void run(IProgressMonitor monitor)
                                throws InvocationTargetException, InterruptedException {
                            try {

                                List<SimpleProject> prj = SimpleProjectContentManager.getManager()
                                        .getSimpleProjects(monitor);
                                projects.addAll(prj);
                            } catch (CoreException e) {
                                throw new InvocationTargetException(e);
                            }
                        }
                    };
                    wizard.getContainer().run(false, false, runnable);
                } catch (InvocationTargetException e) {
                    error[0] = ErrorUtils.getErrorMessage("Failed to load Simple Project template content", e);
                    exception[0] = e;
                } catch (InterruptedException e) {
                    error[0] = "Failure while loading Simple Project templates due to interrupt exception. Template content may not have been loaded correctly.";
                    exception[0] = e;
                }

                if (error[0] != null && exception[0] != null) {

                    PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() {
                        public void run() {
                            setError(error[0], exception[0]);
                        }
                    });
                }

            }
        });
        return projects;

    }

    private void refreshPage(boolean refreshAll) {

        // The content may have changed (e.g. if templates manually refreshed by
        // the user)
        // therefore invoke the content provider again to refresh the contents
        // of the tree viewer
        // with the new contents.
        if (refreshAll) {
            treeViewer.refresh(true);

            boolean needsDownload = false;
            for (Template template : templates) {
                if (!(template instanceof SimpleProject) && !template.getItem().isLocal()) {
                    needsDownload = true;
                    break;
                }
            }

            expandCategory(SIMPLE_PROJECTS_CATEGORY);

            legendImage.setVisible(needsDownload);
            legendText.setVisible(needsDownload);
            descriptionText.setText(""); //$NON-NLS-1$
            refreshButton.setEnabled(true);
        } else {
            refreshSelectedTemplateInViewer();
        }

    }

    public void refreshSelectedTemplateInViewer() {
        Template selectedTemplate = model.selectedTemplate.getValue();
        if (selectedTemplate != null && treeViewer != null && !treeViewer.getTree().isDisposed()) {
            treeViewer.refresh(selectedTemplate, true);
            treeViewer.setSelection(new StructuredSelection(selectedTemplate), true);
        }
    }

    private void setDescription(Template template) {
        String description = null;

        if (template != null) {
            description = template.getDescription();
            // Do not show URL for Simple Projects
            if (!(template instanceof SimpleProject) && template.getItem().getRemoteDescriptor() != null) {
                description += "\n\nURL:" + template.getItem().getRemoteDescriptor().getUrl();
            }
        }

        if (description != null) {
            descriptionText.setText(description);
        } else {
            descriptionText.setText(""); //$NON-NLS-1$
        }
        descriptionText.redraw();
    }

    private String getWarning(Template template) {
        String requiredBundleStr = null;
        ContentItem contentItem = template.getItem();
        if (contentItem.getLocalDescriptor() != null) {
            requiredBundleStr = contentItem.getLocalDescriptor().getRequiresBundle();
        }
        if (requiredBundleStr == null && contentItem.getRemoteDescriptor() != null) {
            requiredBundleStr = contentItem.getRemoteDescriptor().getRequiresBundle();
        }

        StringBuilder missingBundleStr = new StringBuilder();
        if (requiredBundleStr != null) {
            String[] requiredBundles = requiredBundleStr.split(",");
            for (String requiredBundle : requiredBundles) {
                if (Platform.getBundle(requiredBundle.trim()) == null) {
                    if (missingBundleStr.length() > 0) {
                        missingBundleStr.append(", ");
                    }
                    missingBundleStr.append(requiredBundle.trim());
                }
            }
        }

        if (missingBundleStr.length() > 0) {
            String message = NLS.bind("To ensure project compiles properly, please install bundle(s) {0}.",
                    missingBundleStr);
            return message;
        }
        return null;
    }

    public void dispose() {
        Assert.isNotNull(contentManagerListener);
        ContentPlugin.getDefault().getManager().removeListener(contentManagerListener);

    }

    private boolean isRefreshing() {
        return ContentPlugin.getDefault().getManager().isRefreshing();
    }

}