org.jboss.tools.modeshape.rest.wizards.PublishPage.java Source code

Java tutorial

Introduction

Here is the source code for org.jboss.tools.modeshape.rest.wizards.PublishPage.java

Source

/*
 * See the COPYRIGHT.txt file distributed with this work for information
 * regarding copyright ownership.
 *
 * This software is made available by Red Hat, Inc. 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.
 *
 * See the AUTHORS.txt file in the distribution for a full listing of
 * individual contributors.
 */
package org.jboss.tools.modeshape.rest.wizards;

import static org.jboss.tools.modeshape.rest.IUiConstants.HelpContexts.PUBLISH_DIALOG_HELP_CONTEXT;
import static org.jboss.tools.modeshape.rest.IUiConstants.Preferences.ENABLE_RESOURCE_VERSIONING;
import static org.jboss.tools.modeshape.rest.IUiConstants.Preferences.IGNORED_RESOURCES_PREFERENCE;
import static org.jboss.tools.modeshape.rest.IUiConstants.Preferences.PUBLISHING_PREFERENCE_PAGE_ID;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.wizard.IWizard;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
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.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.eclipse.ui.help.IWorkbenchHelpSystem;
import org.jboss.tools.modeshape.rest.Activator;
import org.jboss.tools.modeshape.rest.IServerRegistryListener;
import org.jboss.tools.modeshape.rest.RestClientI18n;
import org.jboss.tools.modeshape.rest.ServerManager;
import org.jboss.tools.modeshape.rest.ServerRegistryEvent;
import org.jboss.tools.modeshape.rest.Utils;
import org.jboss.tools.modeshape.rest.actions.AddPublishAreaAction;
import org.jboss.tools.modeshape.rest.actions.NewServerAction;
import org.jboss.tools.modeshape.rest.domain.ModeShapeRepository;
import org.jboss.tools.modeshape.rest.domain.ModeShapeServer;
import org.jboss.tools.modeshape.rest.domain.ModeShapeWorkspace;
import org.jboss.tools.modeshape.rest.domain.WorkspaceArea;
import org.jboss.tools.modeshape.rest.jobs.PublishJob.Type;
import org.jboss.tools.modeshape.rest.preferences.IgnoredResourcesModel;
import org.jboss.tools.modeshape.rest.preferences.PublishingFileFilter;
import org.modeshape.common.util.CheckArg;
import org.modeshape.common.util.StringUtil;
import org.modeshape.web.jcr.rest.client.Status;
import org.modeshape.web.jcr.rest.client.Status.Severity;

/**
 * The <code>PublishPage</code> is a UI for publishing or unpublishing one or more files to a repository.
 */
public final class PublishPage extends WizardPage implements IServerRegistryListener, ModifyListener {

    /**
     * The key in the wizard <code>IDialogSettings</code> for the recurse flag.
     */
    private static final String RECURSE_KEY = "recurse"; //$NON-NLS-1$

    /**
     * Indicates if the file filter should be used.
     */
    private static boolean filterFiles = true;

    /**
     * @param container the project or folder whose files are being requested
     * @param recurse the flag indicating if child containers should be traversed
     * @param filter the file filter or <code>null</code> if not used
     * @return the list of files contained in the specified container (never <code>null</code>)
     * @throws CoreException if there is a problem finding the files
     */
    private static List<IFile> findFiles(IContainer container, boolean recurse, PublishingFileFilter filter)
            throws CoreException {
        List<IFile> result = new ArrayList<IFile>();

        if (((container instanceof IProject) && !((IProject) container).isOpen())
                || ((filter != null) && !filter.accept(container))) {
            return result;
        }

        // process container members
        for (IResource member : container.members()) {
            if (recurse && (member instanceof IContainer)) {
                // don't select closed projects
                if ((member instanceof IProject) && !((IProject) member).isOpen()) {
                    continue;
                }

                result.addAll(findFiles((IContainer) member, recurse, filter));
            } else if ((member instanceof IFile) && ((IFile) member).getLocation().toFile().exists()) {
                if ((filter == null) || filter.accept(member)) {
                    result.add((IFile) member);
                }
            }
        }

        return result;
    }

    /**
     * Processes the specified list of files and for (1) each file found adds it to the result and (2) for each project or folder
     * adds all contained files. For projects and folders processing will be recursive based on saved wizard settings.
     * 
     * @param resources the resources being processed (never <code>null</code>)
     * @param recurse the flag indicating if child containers should be traversed
     * @param filter the file filter or <code>null</code> if not used
     * @return the files being published or unpublished (never <code>null</code> )
     * @throws CoreException if there is a problem processing the resources
     */
    private static List<IFile> processResources(List<IResource> resources, boolean recurse,
            PublishingFileFilter filter) throws CoreException {
        assert (resources != null);
        List<IFile> result = new ArrayList<IFile>();

        // Project Map - the outer map. Its keys are IProjects and its values
        // are a Parent Map
        // Parent Map - the inner map. Its keys are IContainers (IProject,
        // IFolder) and its values are a list of files
        Map<IProject, Map<IContainer, List<IFile>>> projectMap = new HashMap<IProject, Map<IContainer, List<IFile>>>();

        // Step 1: Process resources
        // - For each file make sure there is a project entry and parent entry
        // then add the file to the Parent Map.
        // - For each folder make sure there is a project entry then add folder
        // entry.
        // - For each project make sure there is a project entry.
        //
        // Step 2: Process maps
        // - In the Project Map, when the recurse flag is set, entries for
        // projects that have a null value (parent map) will be
        // traversed finding all child files and them to results.
        // - In the internal parent map, when the recurse flag is set, entries
        // for parents that have a null value (child
        // collection) will be traversed finding all child files and add them to
        // results.
        //
        // Step 3: Add files from Step 1 to results

        // Step 1 (see above for processing description)
        for (IResource resource : resources) {
            IFile file = null;
            IProject project = null;
            List<IFile> files = null;
            Map<IContainer, List<IFile>> parentMap = null;

            // see if resource is filtered
            if ((filter != null) && !filter.accept(resource)) {
                continue;
            }

            if (resource instanceof IFile) {
                IContainer parent = null; // project or folder
                file = (IFile) resource;
                parent = file.getParent();
                project = file.getProject();

                // make sure there is a project entry
                if (!projectMap.containsKey(project)) {
                    projectMap.put(project, null);
                }

                parentMap = projectMap.get(project);

                // make sure there is a parent entry
                if (parentMap == null) {
                    parentMap = new HashMap<IContainer, List<IFile>>();
                    projectMap.put(project, parentMap);
                }

                files = parentMap.get(parent);

                // make sure there is a files collection
                if (files == null) {
                    files = new ArrayList<IFile>();
                    parentMap.put(parent, files);
                }

                // add file
                files.add(file);
            } else if (resource instanceof IFolder) {
                IFolder folder = (IFolder) resource;
                project = folder.getProject();

                // make sure there is a project entry
                if (!projectMap.containsKey(project)) {
                    projectMap.put(project, null);
                }

                parentMap = projectMap.get(project);

                // make sure there is a folder entry
                if (parentMap == null) {
                    parentMap = new HashMap<IContainer, List<IFile>>();
                    projectMap.put(project, parentMap);
                }

                // add folder only if not already there
                if (!parentMap.containsKey(folder)) {
                    parentMap.put(folder, null);
                }
            } else if (resource instanceof IProject) {
                project = (IProject) resource;

                // if map does not have entry create one
                if (!projectMap.containsKey(project)) {
                    projectMap.put(project, null);
                }
            }
        }

        // Step 2 (see above for processing description)
        // Process projects that have nothing under them selected
        for (IProject project : projectMap.keySet()) {
            Map<IContainer, List<IFile>> parentMap = projectMap.get(project);

            if (parentMap == null) {
                result.addAll(findFiles(project, recurse, filter));
            } else {
                // process folders with no folder entries
                for (IContainer folder : parentMap.keySet()) {
                    List<IFile> files = parentMap.get(folder);

                    if (files == null) {
                        result.addAll(findFiles(folder, recurse, filter));
                    }
                }
            }
        }

        // Step 3 (see above for processing description)
        for (IProject project : projectMap.keySet()) {
            Map<IContainer, List<IFile>> parentMap = projectMap.get(project);

            if (parentMap != null) {
                for (Entry<IContainer, List<IFile>> entry : parentMap.entrySet()) {
                    if (entry.getValue() != null) {
                        result.addAll(entry.getValue());
                    }
                }
            }
        }

        return result;
    }

    /**
     * The button that allows user to create a new publish area.
     */
    private Button btnNewArea;

    /**
     * The repository chooser control.
     */
    private Combo cbxRepository;

    /**
     * The server chooser control.
     */
    private Combo cbxServer;

    /**
     * The workspace chooser control.
     */
    private Combo cbxWorkspace;

    /**
     * The workspace area chooser control.
     */
    private Combo cbxWorkspaceAreas;

    /**
     * The control indicating if the user wants to version resources (will be <code>null</code> when unpublishing).
     */
    private Button chkVersioning;

    /**
     * The files being published or unpublished (never <code>null</code>).
     */
    private List<IFile> files;

    /**
     * The filter used to determine if a file should be included in publishing operations (may be <code>null</code>).
     */
    private PublishingFileFilter filter;

    /**
     * A hyperlink to the preference page (will be <code>null</code> when unpublishing).
     */
    private Link linkPrefs;

    /**
     * The control containing all the files being published or unpublished.
     */
    private org.eclipse.swt.widgets.List lstResources;

    /**
     * Indicates if resources should be found recursively.
     */
    private boolean recurse = true;

    /**
     * A collection of repositories for the selected server (never <code>null</code>).
     */
    private List<ModeShapeRepository> repositories;

    /**
     * The repository where the workspace is located.
     */
    private ModeShapeRepository repository;

    /**
     * <code>true</code> if the selected repository supports versioning
     */
    private boolean repositorySupportsVersioning;

    /**
     * The collection of resources selected by the user to be published or unpublished.
     */
    private final List<IResource> resources;

    /**
     * The server where the repository is located.
     */
    private ModeShapeServer server;

    /**
     * A collection of servers from the server registry (never <code>null</code> ).
     */
    private List<ModeShapeServer> servers;

    /**
     * The current validation status.
     */
    private Status status;

    /**
     * Indicates if publishing or unpublishing is being done.
     */
    private final Type type;

    /**
     * Indicates if versioning of published resources should be done.
     */
    private boolean versioning = true;

    /**
     * The workspace where the resources are being published/unpublished (may be <code>null</code>).
     */
    private ModeShapeWorkspace workspace;

    /**
     * The path segment prepended to the resource project path.
     */
    private String workspaceArea;

    /**
     * A collection of workspaces for the selected server repository (never <code>null</code>).
     */
    private List<ModeShapeWorkspace> workspaces;

    /**
     * @param type indicates if publishing or unpublishing is being done
     * @param resources the resources being published or unpublished (never <code>null</code>)
     * @throws CoreException if there is a problem processing the input resources
     */
    public PublishPage(Type type, List<IResource> resources) throws CoreException {
        super(PublishPage.class.getSimpleName());
        CheckArg.isNotNull(resources, "resources"); //$NON-NLS-1$
        setTitle((type == Type.PUBLISH) ? RestClientI18n.publishPagePublishTitle
                : RestClientI18n.publishPageUnpublishTitle);
        setPageComplete(false);

        this.type = type;
        this.resources = resources;

        // load filter with current preference value
        IgnoredResourcesModel model = new IgnoredResourcesModel();
        model.load(Activator.getDefault().getPreferenceStore().getString(IGNORED_RESOURCES_PREFERENCE));
        this.filter = (filterFiles ? new PublishingFileFilter(model) : null);
    }

    private void constructLocationPanel(Composite parent) {
        Group pnl = new Group(parent, SWT.NONE);
        pnl.setText(RestClientI18n.publishPageLocationGroupTitle);
        pnl.setLayout(new GridLayout(2, false));
        pnl.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));

        // row 1: label combobox button
        // row 2: label combobox
        // row 3: label combobox
        // row 4: label combobox

        { // row 1: server row
            Composite pnlServer = new Composite(pnl, SWT.NONE);
            GridLayout layout = new GridLayout(3, false);
            layout.marginHeight = 0;
            layout.marginWidth = 0;
            pnlServer.setLayout(layout);
            GridData gd = new GridData(SWT.FILL, SWT.CENTER, true, false);
            gd.horizontalSpan = 2;
            pnlServer.setLayoutData(gd);

            Label lblServer = new Label(pnlServer, SWT.LEFT);
            lblServer.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
            lblServer.setText(RestClientI18n.publishPageServerLabel);
            lblServer.setToolTipText(RestClientI18n.publishPageServerToolTip);

            this.cbxServer = new Combo(pnlServer, SWT.DROP_DOWN | SWT.READ_ONLY);
            this.cbxServer.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
            this.cbxServer.setToolTipText(RestClientI18n.publishPageServerToolTip);

            final IAction action = new NewServerAction(this.getShell(), getServerManager());
            final Button btnNewServer = new Button(pnlServer, SWT.PUSH);
            btnNewServer.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
            btnNewServer.setText(RestClientI18n.publishPageNewServerButton);
            btnNewServer.setToolTipText(action.getToolTipText());
            btnNewServer.addSelectionListener(new SelectionAdapter() {
                /**
                 * {@inheritDoc}
                 * 
                 * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
                 */
                @Override
                public void widgetSelected(SelectionEvent e) {
                    action.run();
                }
            });

            // update page message first time selected to get rid of initial message by forcing validation
            btnNewServer.addSelectionListener(new SelectionAdapter() {
                /**
                 * {@inheritDoc}
                 * 
                 * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
                 */
                @Override
                public void widgetSelected(SelectionEvent e) {
                    updateInitialMessage();
                    btnNewServer.removeSelectionListener(this);
                }
            });
        }

        { // row 2: repository row
            Label lblRepository = new Label(pnl, SWT.LEFT);
            lblRepository.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
            lblRepository.setText(RestClientI18n.publishPageRepositoryLabel);
            lblRepository.setToolTipText(RestClientI18n.publishPageRepositoryToolTip);

            this.cbxRepository = new Combo(pnl, SWT.DROP_DOWN | SWT.READ_ONLY);
            this.cbxRepository.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
            this.cbxRepository.setToolTipText(RestClientI18n.publishPageRepositoryToolTip);
        }

        { // row 3: workspace row
            Label lblWorkspace = new Label(pnl, SWT.LEFT);
            lblWorkspace.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
            lblWorkspace.setText(RestClientI18n.publishPageWorkspaceLabel);

            this.cbxWorkspace = new Combo(pnl, SWT.DROP_DOWN | SWT.READ_ONLY);
            this.cbxWorkspace.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));

            if (type == Type.PUBLISH) {
                this.cbxWorkspace.setToolTipText(RestClientI18n.publishPageWorkspacePublishToolTip);
            } else {
                this.cbxWorkspace.setToolTipText(RestClientI18n.publishPageWorkspaceUnpublishToolTip);
            }

            this.cbxWorkspace.setToolTipText(this.cbxWorkspace.getToolTipText());
        }

        { // row 4: workspace area
            Composite pnlArea = new Composite(parent, SWT.NONE);
            GridLayout layout = new GridLayout(3, false);
            layout.marginHeight = 0;
            layout.marginWidth = 0;
            pnlArea.setLayout(layout);
            pnlArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
            ((GridData) pnl.getLayoutData()).horizontalSpan = 2;

            Label lblWorkspaceArea = new Label(pnlArea, SWT.LEFT);
            lblWorkspaceArea.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
            lblWorkspaceArea.setText(RestClientI18n.publishPageWorkspaceAreaLabel);
            lblWorkspaceArea.setToolTipText(RestClientI18n.publishPageWorkspaceAreaToolTip);

            this.cbxWorkspaceAreas = new Combo(pnlArea, SWT.DROP_DOWN | SWT.READ_ONLY);
            this.cbxWorkspaceAreas.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
            this.cbxWorkspaceAreas.setToolTipText(RestClientI18n.publishPageWorkspaceAreaToolTip);
            this.cbxWorkspaceAreas.setEnabled(false);

            this.btnNewArea = new Button(pnlArea, SWT.PUSH);
            this.btnNewArea.setImage(Activator.getDefault().getSharedImage(ISharedImages.IMG_OBJ_ADD));
            this.btnNewArea.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
            this.btnNewArea.setToolTipText(RestClientI18n.addPublishAreaActionToolTip);
            this.btnNewArea.setEnabled(false);
            this.btnNewArea.addSelectionListener(new SelectionAdapter() {

                /**
                 * {@inheritDoc}
                 * 
                 * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
                 */
                @Override
                public void widgetSelected(SelectionEvent e) {
                    handleNewWorkspaceArea();
                }
            });
        }
    }

    private void constructResourcesPanel(Composite parent) {
        Composite pnl = new Composite(parent, SWT.NONE);
        pnl.setLayout(new GridLayout());
        pnl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

        // pnl layout:
        // row 1: lbl
        // row 2: lstResources
        // row 3: recurse chkbox
        // row 4: versioning chkbox and link (only when publishing)

        { // row 1
            Label lbl = new Label(pnl, SWT.LEFT);
            lbl.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));

            if (type == Type.PUBLISH) {
                lbl.setText(RestClientI18n.publishPagePublishResourcesLabel);
            } else {
                lbl.setText(RestClientI18n.publishPageUnpublishResourcesLabel);
            }
        }

        { // row 2
            this.lstResources = new org.eclipse.swt.widgets.List(pnl, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
            GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true);
            gd.horizontalSpan = 2;
            gd.minimumHeight = this.lstResources.getItemHeight() * 2; // set min height
            gd.heightHint = this.lstResources.getItemHeight() * 10; // set preferred height
            this.lstResources.setLayoutData(gd);
            final org.eclipse.swt.widgets.List finalLst = this.lstResources;

            // update page message first time selected to get rid of initial message by forcing validation
            this.lstResources.addSelectionListener(new SelectionAdapter() {
                /**
                 * {@inheritDoc}
                 * 
                 * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
                 */
                @Override
                public void widgetSelected(SelectionEvent e) {
                    // do the very first time to get rid of initial message then
                    // remove listener
                    updateInitialMessage();
                    finalLst.removeSelectionListener(this);
                }
            });

            // load list with initial files
            loadFiles();
        }

        { // row 3 recurse chkbox
            Button chkRecurse = new Button(pnl, SWT.CHECK);
            chkRecurse.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
            chkRecurse.setText(RestClientI18n.publishPageRecurseCheckBox);
            chkRecurse.setToolTipText(RestClientI18n.publishPageRecurseCheckBoxToolTip);

            // set the recurse flag based on dialog settings
            if (getDialogSettings().get(RECURSE_KEY) != null) {
                this.recurse = getDialogSettings().getBoolean(RECURSE_KEY);
            }

            chkRecurse.setSelection(this.recurse);
            chkRecurse.addSelectionListener(new SelectionAdapter() {
                /**
                 * {@inheritDoc}
                 * 
                 * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
                 */
                @Override
                public void widgetSelected(SelectionEvent e) {
                    handleRecurseChanged(((Button) e.widget).getSelection());
                }
            });

            // update page message first time selected to get rid of initial message by forcing validation
            chkRecurse.addSelectionListener(new SelectionAdapter() {
                /**
                 * {@inheritDoc}
                 * 
                 * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
                 */
                @Override
                public void widgetSelected(SelectionEvent e) {
                    updateInitialMessage();
                    ((Button) e.widget).removeSelectionListener(this);
                }
            });
        }

        if (this.type == Type.PUBLISH) {
            // row 4 versioning chkbox and link to open preference page
            Composite pnlVersioning = new Composite(pnl, SWT.NONE);
            pnlVersioning.setLayout(new GridLayout(2, false));
            ((GridLayout) pnlVersioning.getLayout()).marginWidth = 0;
            pnlVersioning.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true));
            ((GridData) pnlVersioning.getLayoutData()).minimumHeight = 30;

            this.chkVersioning = new Button(pnlVersioning, SWT.CHECK);
            this.chkVersioning.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
            this.chkVersioning.setText(RestClientI18n.publishPageVersionCheckBox);
            this.chkVersioning.setToolTipText(RestClientI18n.publishPageVersionCheckBoxToolTip);

            // set the version flag based on preference
            this.versioning = Activator.getDefault().getPreferenceStore().getBoolean(ENABLE_RESOURCE_VERSIONING);

            this.chkVersioning.setSelection(this.versioning);
            this.chkVersioning.setEnabled(false);
            this.chkVersioning.addSelectionListener(new SelectionAdapter() {
                /**
                 * {@inheritDoc}
                 * 
                 * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
                 */
                @Override
                public void widgetSelected(SelectionEvent e) {
                    handleVersioningChanged(((Button) e.widget).getSelection());
                }
            });

            this.linkPrefs = new Link(pnlVersioning, SWT.WRAP);
            this.linkPrefs.setText(RestClientI18n.publishPageOpenPreferencePageLink);
            this.linkPrefs.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
            this.linkPrefs.setEnabled(false);
            this.linkPrefs.addSelectionListener(new SelectionAdapter() {
                /**
                 * {@inheritDoc}
                 * 
                 * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
                 */
                @Override
                public void widgetSelected(SelectionEvent e) {
                    handleOpenPreferencePage();
                }
            });
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
     */
    @Override
    public void createControl(Composite parent) {
        Composite pnlMain = new Composite(parent, SWT.NONE);
        pnlMain.setLayout(new GridLayout());
        pnlMain.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
        constructLocationPanel(pnlMain);
        constructResourcesPanel(pnlMain);
        setControl(pnlMain);

        // add combobox listeners
        this.cbxRepository.addModifyListener(this);
        this.cbxServer.addModifyListener(this);
        this.cbxWorkspace.addModifyListener(this);
        this.cbxWorkspaceAreas.addModifyListener(this);

        // register with the help system
        IWorkbenchHelpSystem helpSystem = Activator.getDefault().getWorkbench().getHelpSystem();
        helpSystem.setHelp(pnlMain, PUBLISH_DIALOG_HELP_CONTEXT);

        // register to receive server registry events (this will populate the UI)
        getServerManager().addRegistryListener(this);
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.eclipse.jface.dialogs.DialogPage#dispose()
     */
    @Override
    public void dispose() {
        getServerManager().removeRegistryListener(this);
        super.dispose();
    }

    /**
     * @return the files to publish or unpublish (never <code>null</code>)
     */
    List<IFile> getFiles() {
        return this.files;
    }

    /**
     * @return the server manager obtained from the wizard
     */
    private ServerManager getServerManager() {
        return ((PublishWizard) getWizard()).getServerManager();
    }

    /**
     * @return the workspace to use when publishing or unpublishing (page must be complete)
     */
    ModeShapeWorkspace getWorkspace() {
        assert isPageComplete();
        return this.workspace;
    }

    /**
     * @return the path segment prepended to the resource project path (never <code>null</code> but can be empty)
     */
    String getWorkspaceArea() {
        return this.workspaceArea;
    }

    /**
     * Opens dialog that will create a new workspace area.
     */
    void handleNewWorkspaceArea() {
        final AddPublishAreaAction action = new AddPublishAreaAction(getShell(), getServerManager(), this.workspace,
                Utils.getServerViewer());
        action.run();

        if (action.success()) {
            String newPath = null;
            final String[] before = this.cbxWorkspaceAreas.getItems();
            final WorkspaceArea[] workspaceAreas = workspaceAreas();
            final List<String> items = new ArrayList<String>();

            // add in those workspace areas identified by server
            if ((workspaceAreas != null) && (workspaceAreas.length != 0)) {
                for (final WorkspaceArea area : workspaceAreas) {
                    boolean found = false;
                    final String path = area.getPath();
                    items.add(path);

                    // keep track of the new path added
                    if (StringUtil.isBlank(newPath)) {
                        for (final String beforeArea : before) {
                            if (beforeArea.equals(path)) {
                                found = true;
                                break;
                            }
                        }

                        if (!found) {
                            newPath = path;
                        }
                    }
                }

                Collections.sort(items);
            }

            // populate combo
            this.cbxWorkspaceAreas.setItems(items.toArray(new String[items.size()]));

            // should always have a new path unless server went down
            if (!StringUtil.isBlank(newPath)) {
                this.cbxWorkspaceAreas.setText(newPath);
            }
        }
    }

    /**
     * Opens the preference page.
     */
    void handleOpenPreferencePage() {
        // open preference page and only allow the pref page where the version setting is
        PreferencesUtil.createPreferenceDialogOn(getShell(), PUBLISHING_PREFERENCE_PAGE_ID,
                new String[] { PUBLISHING_PREFERENCE_PAGE_ID }, null).open();
    }

    /**
     * Saves the recurse setting and reloads the files to be published or unpublished.
     * 
     * @param selected the flag indicating the new recurse setting
     */
    void handleRecurseChanged(boolean selected) {
        this.recurse = selected;

        try {
            this.files = processResources(this.resources, isRecursing(), filter);
            loadFiles();
        } catch (CoreException e) {
            Activator.getDefault()
                    .log(new Status(Severity.ERROR, RestClientI18n.publishPageRecurseProcessingErrorMsg, e));

            if (getControl().isVisible()) {
                MessageDialog.openError(getShell(), RestClientI18n.errorDialogTitle,
                        RestClientI18n.publishPageRecurseProcessingErrorMsg);
            }
        }
    }

    /**
     * Handler for when the repository control value is modified
     */
    void handleRepositoryModified() {
        int index = this.cbxRepository.getSelectionIndex();

        // make sure there is a selection
        if (index != -1) {
            // since repositories are sorted need to find right one
            final String repoName = this.cbxRepository.getText();

            for (ModeShapeRepository repo : this.repositories) {
                if (repoName.equals(repo.getName())) {
                    this.repository = repo;
                    break;
                }
            }
        }

        // repository capabilities could affect the UI
        updateRepositoryCapabilities();

        // clear loaded workspaces
        refreshWorkspaces();

        // update page state
        updateState();
    }

    /**
     * Handler for when the server control value is modified
     */
    void handleServerModified() {
        int index = this.cbxServer.getSelectionIndex();

        // make sure there is a selection
        if (index != -1) {
            this.server = this.servers.get(index);
        }

        // need to reload repositories since server changed
        refreshRepositories();

        // update page state
        updateState();
    }

    /**
     * Saves the versioning setting.
     * 
     * @param selected the flag indicating the new versioning setting
     */
    void handleVersioningChanged(boolean selected) {
        this.versioning = selected;
    }

    /**
     * Handler for when the workspace area value is modified.
     */
    void handleWorkspaceAreaModified() {
        int index = this.cbxWorkspaceAreas.getSelectionIndex();

        if (index == -1) {
            this.workspaceArea = this.cbxWorkspaceAreas.getText();
        } else {
            this.workspaceArea = this.cbxWorkspaceAreas.getItems()[index];
        }
    }

    WorkspaceArea[] workspaceAreas() {
        try {
            final WorkspaceArea[] workspaceAreas = getServerManager().getWorkspaceAreas(this.workspace);
            return workspaceAreas;
        } catch (Exception e) {
            Activator.getDefault().log(new Status(Severity.ERROR,
                    NLS.bind(RestClientI18n.publishPageUnableToObtainWorkspaceAreas, this.workspace), e));
        }

        return new WorkspaceArea[0];
    }

    /**
     * Handler for when the workspace control value is modified.
     */
    void handleWorkspaceModified() {
        int index = this.cbxWorkspace.getSelectionIndex();

        // make sure there is a selection
        if (index != -1) {
            final String workspaceName = this.cbxWorkspace.getText();

            // since workspaces are sorted need to find the right one
            for (ModeShapeWorkspace testWorkspace : this.workspaces) {
                if (workspaceName.equals(testWorkspace.getName())) {
                    this.workspace = testWorkspace;
                    break;
                }
            }

            // update workspace areas from server
            final WorkspaceArea[] workspaceAreas = workspaceAreas();
            final List<String> items = new ArrayList<String>();

            // add in those workspace areas identified by server
            if ((workspaceAreas != null) && (workspaceAreas.length != 0)) {
                for (final WorkspaceArea area : workspaceAreas) {
                    final String path = area.getPath();
                    items.add(path);
                }

                Collections.sort(items);
            }

            // populate combo
            this.cbxWorkspaceAreas.setItems(items.toArray(new String[items.size()]));

            if (!this.cbxWorkspaceAreas.isEnabled()) {
                this.cbxWorkspaceAreas.setEnabled(true);
                this.btnNewArea.setEnabled(true);
            }
        } else {
            this.workspaceArea = null;
            this.cbxWorkspaceAreas.removeAll();

            if (this.cbxWorkspaceAreas.isEnabled()) {
                this.cbxWorkspaceAreas.setEnabled(false);
                this.btnNewArea.setEnabled(false);
            }
        }

        if (this.cbxWorkspaceAreas.getItemCount() == 0) {
            this.workspaceArea = null;
        } else {
            this.cbxWorkspaceAreas.select(0);
        }

        updateState();
    }

    /**
     * @return <code>true</code> if resources found recursively under projects and folders should also be published or unpublished
     */
    boolean isRecursing() {
        return this.recurse;
    }

    /**
     * @return <code>true</code> if versioning of published resources should be done
     */
    boolean isVersioning() {
        return (this.repositorySupportsVersioning && this.versioning);
    }

    /**
     * Populates the list of files to be published based on the recurse flag and the list of workspace selected resources.
     * Pre-condition is that {@link #processResources(List, boolean, PublishingFileFilter)} has been called.
     */
    private void loadFiles() {
        this.lstResources.removeAll();

        for (IResource resource : this.files) {
            this.lstResources.add(resource.getFullPath().toString());
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.eclipse.swt.events.ModifyListener#modifyText(org.eclipse.swt.events.ModifyEvent)
     */
    @Override
    public void modifyText(ModifyEvent e) {
        if (e.widget == this.cbxServer) {
            handleServerModified();
        } else if (e.widget == this.cbxRepository) {
            handleRepositoryModified();
        } else if (e.widget == this.cbxWorkspace) {
            handleWorkspaceModified();
        } else if (e.widget == this.cbxWorkspaceAreas) {
            handleWorkspaceAreaModified();
        } else {
            assert false; // should not happen
        }
    }

    /**
     * Refreshes the repository-related fields and controls based on the server registry. This in turn causes the workspaces to
     * also to be refreshed.
     */
    private void refreshRepositories() {
        this.repository = null;

        if (this.server == null) {
            this.repositories = Collections.emptyList();
        } else {
            try {
                this.repositories = new ArrayList<ModeShapeRepository>(
                        getServerManager().getRepositories(this.server));
            } catch (Exception e) {
                this.repositories = Collections.emptyList();
                String msg = NLS.bind(RestClientI18n.serverManagerGetRepositoriesExceptionMsg,
                        this.server.getShortDescription());
                Activator.getDefault().log(new Status(Severity.ERROR, msg, e));
            }
        }

        // clear items
        this.cbxRepository.removeAll();

        // reload
        if (this.repositories.isEmpty()) {
            // disable control if necessary
            if (this.cbxRepository.getEnabled()) {
                this.cbxRepository.setEnabled(false);
            }
        } else if (this.repositories.size() == 1) {
            this.repository = this.repositories.get(0);
            this.cbxRepository.add(this.repository.getName());
            this.cbxRepository.select(0);

            // enable control if necessary
            if (!this.cbxRepository.getEnabled()) {
                this.cbxRepository.setEnabled(true);
            }
        } else {
            // add an item for each repository
            final List<String> repoNames = new ArrayList<String>(this.repositories.size());

            for (ModeShapeRepository repository : this.repositories) {
                repoNames.add(repository.getName());
            }

            Collections.sort(repoNames);
            this.cbxRepository.setItems(repoNames.toArray(new String[repoNames.size()]));

            // enable control if necessary
            if (!this.cbxRepository.getEnabled()) {
                this.cbxRepository.setEnabled(true);
            }
        }

        // repository capabilities could affect the UI
        updateRepositoryCapabilities();

        // must reload workspaces
        refreshWorkspaces();
    }

    /**
     * Refreshes the server-related fields and controls based on the server registry. This in turn causes the repositories and
     * workspaces to also to be refreshed.
     */
    void refreshServers() {
        this.server = null;
        this.servers = new ArrayList<ModeShapeServer>(getServerManager().getServers());

        // clear server combo
        this.cbxServer.removeAll();

        if (this.servers.size() == 0) {
            // disable control if necessary
            if (this.cbxServer.getEnabled()) {
                this.cbxServer.setEnabled(false);
            }
        } else if (this.servers.size() == 1) {
            this.server = this.servers.get(0);
            this.cbxServer.add(this.server.getName());
            this.cbxServer.select(0);

            // enable control if necessary
            if (!this.cbxServer.getEnabled()) {
                this.cbxServer.setEnabled(true);
            }
        } else {
            // add an item for each server
            for (ModeShapeServer server : this.servers) {
                this.cbxServer.add(server.getName());
            }

            // enable control if necessary
            if (!this.cbxServer.getEnabled()) {
                this.cbxServer.setEnabled(true);
            }
        }

        // must reload repositories
        refreshRepositories();
    }

    /**
     * Refreshes the workspace-related fields and controls based on the server registry.
     */
    private void refreshWorkspaces() {
        this.workspace = null;

        if (this.repository == null) {
            this.workspaces = Collections.emptyList();
        } else {
            try {
                this.workspaces = new ArrayList<ModeShapeWorkspace>(
                        getServerManager().getWorkspaces(this.repository));
            } catch (Exception e) {
                this.workspaces = Collections.emptyList();
                String msg = NLS.bind(RestClientI18n.serverManagerGetWorkspacesExceptionMsg, this.repository);
                Activator.getDefault().log(new Status(Severity.ERROR, msg, e));
            }
        }

        // clear items
        this.cbxWorkspace.removeAll();
        this.cbxWorkspaceAreas.removeAll();

        // reload
        if (this.workspaces.isEmpty()) {
            // disable controls if necessary
            if (this.cbxWorkspace.getEnabled()) {
                this.cbxWorkspace.setEnabled(false);
            }

            if (this.cbxWorkspaceAreas.isEnabled()) {
                this.cbxWorkspaceAreas.setEnabled(false);
                this.btnNewArea.setEnabled(false);
            }
        } else if (this.workspaces.size() == 1) {
            ModeShapeWorkspace temp = this.workspaces.get(0);
            this.cbxWorkspace.add(temp.getName());
            this.cbxWorkspace.select(0);

            // enable controls if necessary
            if (!this.cbxWorkspace.getEnabled()) {
                this.cbxWorkspace.setEnabled(true);
            }

            final boolean enable = (this.cbxWorkspaceAreas.getItemCount() != 0);

            if (this.cbxWorkspaceAreas.getEnabled() != enable) {
                this.cbxWorkspaceAreas.setEnabled(enable);
                this.btnNewArea.setEnabled(enable);
            }
        } else {
            // add an item for each workspace
            final List<String> workspaceNames = new ArrayList<String>(this.workspaces.size());

            for (ModeShapeWorkspace workspace : this.workspaces) {
                workspaceNames.add(workspace.getName());
            }

            Collections.sort(workspaceNames);
            this.cbxWorkspace.setItems(workspaceNames.toArray(new String[workspaceNames.size()]));

            // enable controls if necessary
            if (!this.cbxWorkspace.getEnabled()) {
                this.cbxWorkspace.setEnabled(true);
            }

            if (this.cbxWorkspaceAreas.isEnabled()) {
                this.cbxWorkspaceAreas.setEnabled(false);
                this.btnNewArea.setEnabled(false);
            }
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.jboss.tools.modeshape.rest.IServerRegistryListener#serverRegistryChanged(org.jboss.tools.modeshape.rest.ServerRegistryEvent)
     */
    @Override
    public Exception[] serverRegistryChanged(ServerRegistryEvent event) {
        // should only be a new server event
        if (event.isNew()) {
            refreshServers();
            updateState();
        }

        return null;
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.eclipse.jface.dialogs.DialogPage#setVisible(boolean)
     */
    @Override
    public void setVisible(boolean visible) {
        super.setVisible(visible);

        if (visible) {
            // set initial status
            validate();

            // update OK/Finish button enablement
            setPageComplete(!this.status.isError());

            // set initial message
            if (this.status.isOk()) {
                String msg = ((this.type == Type.PUBLISH) ? RestClientI18n.publishPagePublishOkStatusMsg
                        : RestClientI18n.publishPageUnpublishOkStatusMsg);
                setMessage(msg, IMessageProvider.NONE);
            } else {
                setMessage(this.status.getMessage(), IMessageProvider.ERROR);
            }
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.eclipse.jface.wizard.WizardPage#setWizard(org.eclipse.jface.wizard.IWizard)
     */
    @Override
    public void setWizard(IWizard newWizard) {
        super.setWizard(newWizard);

        try {
            this.files = processResources(this.resources, isRecursing(), this.filter);
        } catch (CoreException e) {
            Activator.getDefault()
                    .log(new Status(Severity.ERROR, RestClientI18n.publishPageRecurseProcessingErrorMsg, e));
        }
    }

    /**
     * Updates the initial page message.
     */
    void updateInitialMessage() {
        String msg = ((this.type == Type.PUBLISH) ? RestClientI18n.publishPagePublishOkStatusMsg
                : RestClientI18n.publishPageUnpublishOkStatusMsg);

        if (msg.equals(getMessage())) {
            updateState();
        }
    }

    /**
     * Some capabilities (like versioning) will not be supported by all repositories. This could affect the UI.
     */
    private void updateRepositoryCapabilities() {
        // versioning
        this.repositorySupportsVersioning = true;

        if (this.repository == null) {
            this.repositorySupportsVersioning = false;
        } else {
            Object supportsVersioning = this.repository.getProperties()
                    .get(javax.jcr.Repository.OPTION_VERSIONING_SUPPORTED);

            if (supportsVersioning == null) {
                this.repositorySupportsVersioning = false;
            } else {
                this.repositorySupportsVersioning = Boolean.parseBoolean(supportsVersioning.toString());
            }
        }

        // update enabled state of versioning controls
        if ((this.chkVersioning != null)
                && (this.chkVersioning.getEnabled() != this.repositorySupportsVersioning)) {
            this.chkVersioning.setEnabled(this.repositorySupportsVersioning);
            this.linkPrefs.setEnabled(this.repositorySupportsVersioning);
        }
    }

    /**
     * Updates message, message icon, and OK button enablement based on validation results
     */
    void updateState() {
        // get the current state
        validate();

        // update OK/Finish button enablement
        setPageComplete(!this.status.isError());

        // update page message
        if (this.status.isError()) {
            setMessage(this.status.getMessage(), IMessageProvider.ERROR);
        } else {
            if (this.status.isWarning()) {
                setMessage(this.status.getMessage(), IMessageProvider.WARNING);
            } else if (this.status.isInfo()) {
                setMessage(this.status.getMessage(), IMessageProvider.INFORMATION);
            } else {
                setMessage(this.status.getMessage(), IMessageProvider.NONE);
            }
        }
    }

    /**
     * Validates all inputs and sets the validation status.
     */
    private void validate() {
        String msg = null;
        Severity severity = Severity.ERROR;

        if ((this.resources == null) || this.resources.isEmpty() || this.files.isEmpty()) {
            msg = ((type == Type.PUBLISH) ? RestClientI18n.publishPageNoResourcesToPublishStatusMsg
                    : RestClientI18n.publishPageNoResourcesToUnpublishStatusMsg);
        } else if (this.server == null) {
            int count = this.cbxServer.getItemCount();
            msg = ((count == 0) ? RestClientI18n.publishPageNoAvailableServersStatusMsg
                    : RestClientI18n.publishPageMissingServerStatusMsg);
        } else if (this.repository == null) {
            int count = this.cbxRepository.getItemCount();
            msg = ((count == 0) ? RestClientI18n.publishPageNoAvailableRepositoriesStatusMsg
                    : RestClientI18n.publishPageMissingRepositoryStatusMsg);
        } else if (this.workspace == null) {
            int count = this.cbxWorkspace.getItemCount();
            msg = ((count == 0) ? RestClientI18n.publishPageNoAvailableWorkspacesStatusMsg
                    : RestClientI18n.publishPageMissingWorkspaceStatusMsg);
        } else if (this.workspaceArea == null) {
            msg = RestClientI18n.publishPageMissingPublishAreaStatusMsg;
        } else {
            severity = Severity.OK;
            msg = ((type == Type.PUBLISH) ? RestClientI18n.publishPagePublishOkStatusMsg
                    : RestClientI18n.publishPageUnpublishOkStatusMsg);
        }

        this.status = new Status(severity, msg, null);
    }

    /**
     * Processing done after wizard is finished. Wizard was not canceled.
     */
    void wizardFinished() {
        // update dialog settings
        getDialogSettings().put(RECURSE_KEY, this.recurse);
    }

}