com.aptana.ide.core.ui.preferences.ProjectNaturesPage.java Source code

Java tutorial

Introduction

Here is the source code for com.aptana.ide.core.ui.preferences.ProjectNaturesPage.java

Source

/*******************************************************************************
    
 * Copyright (c) 2007 Gunnar Wagenknecht and others.
    
 * All rights reserved. This program and the accompanying materials
    
 * are made available under the terms of the Eclipse Public License v1.0
    
 * which accompanies this distribution, and is available at
    
 * http://www.eclipse.org/legal/epl-v10.html
    
 *
    
 * Contributors:
    
 *     IBM Corporation - initial API and implementation
    
 *     Gunnar Wagenknecht - initial API and implementation
     
 *     Shalom Gibly - Aptana additions and modifications
    
 ******************************************************************************/

package com.aptana.ide.core.ui.preferences;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IProjectNatureDescriptor;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.preference.PreferencePage;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.IFontProvider;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.actions.CloseResourceAction;
import org.eclipse.ui.dialogs.PropertyPage;
import org.eclipse.ui.internal.ide.DialogUtil;
import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
import org.eclipse.ui.internal.progress.ProgressMonitorJobsDialog;
import org.eclipse.ui.model.BaseWorkbenchContentProvider;
import org.eclipse.ui.progress.UIJob;

import com.aptana.ide.core.IdeLog;
import com.aptana.ide.epl.Activator;

/**
 * Project property page for viewing and modifying the project natures.
 * 
 * @since Aptana Studio 1.2.4
 */

public class ProjectNaturesPage extends PropertyPage implements ICheckStateListener {

    protected static final String APTANA_NATURE_PREFIX = "com.aptana.ide.";//$NON-NLS-1$;
    private static final int NATURES_LIST_MULTIPLIER = 30;
    private Image aptanaNatureImage = Activator.getImage("icons/aptana_nature.gif"); //$NON-NLS-1$;
    private IProject project;
    private boolean modified = false;
    // widgets
    private CheckboxTableViewer listViewer;
    protected boolean showAptanaOnly;
    private List<String> projectNatures;

    private HashMap<Object, TableItem> maintainChecked = new HashMap<Object, TableItem>();
    private HashMap<String, String> descriptionCache = new HashMap<String, String>();
    private String primaryNature;
    private Button makePrimaryButton;
    private Button restoreButton;
    private List<String> initialCheckedItems;

    public ProjectNaturesPage() {
        showAptanaOnly = true;
    }

    /**
     * @see PreferencePage#createContents
     */
    protected Control createContents(Composite parent) {
        Font font = parent.getFont();
        Composite composite = new Composite(parent, SWT.NONE);
        GridLayout layout = new GridLayout();
        composite.setLayout(layout);
        composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
        composite.setFont(font);
        initialize();
        Label description = createDescriptionLabel(composite);
        description.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));

        Composite tableComposite = new Composite(composite, SWT.NONE);
        layout = new GridLayout(2, false);
        tableComposite.setLayout(layout);
        GridData data = new GridData(GridData.FILL_BOTH);
        tableComposite.setLayoutData(data);

        listViewer = CheckboxTableViewer.newCheckList(tableComposite, SWT.TOP | SWT.BORDER);
        Table table = listViewer.getTable();
        table.setFont(font);
        TableColumn column = new TableColumn(table, SWT.LEFT);
        column.setWidth(350);
        data = new GridData(GridData.FILL_BOTH);
        data.grabExcessHorizontalSpace = true;
        if (!project.isOpen())
            listViewer.getControl().setEnabled(false);

        // Only set a height hint if it will not result in a cut off dialog

        if (DialogUtil.inRegularFontMode(parent)) {
            data.heightHint = getDefaultFontHeight(table, NATURES_LIST_MULTIPLIER);
        }
        table.setLayoutData(data);
        table.setFont(font);
        listViewer.setLabelProvider(getLabelProvider());
        listViewer.setContentProvider(getContentProvider(project));
        listViewer.setComparator(getViewerComperator());
        listViewer.setInput(project.getWorkspace());
        listViewer.addCheckStateListener(this);
        table.setMenu(createMenu());
        initialCheckedItems = initializeCheckedNatures();
        Collections.sort(initialCheckedItems);
        listViewer.setCheckedElements(initialCheckedItems.toArray());
        listViewer.addSelectionChangedListener(new ISelectionChangedListener() {
            public void selectionChanged(SelectionChangedEvent event) {
                updateButtons();
            }
        });

        // Add the buttons
        Composite buttons = new Composite(tableComposite, SWT.NONE);
        layout = new GridLayout(1, true);
        layout.marginWidth = 0;
        layout.marginHeight = 0;
        buttons.setLayout(layout);
        buttons.setLayoutData(new GridData(GridData.FILL_VERTICAL));
        makePrimaryButton = createButton(EPLMessages.ProjectNaturesPage_LBL_MakePrimary, buttons,
                new MakePrimarySelectionListener());
        restoreButton = createButton(EPLMessages.ProjectNaturesPage_LBL_Restore, buttons,
                new RestoreSelectionListener());
        updateButtons();

        return composite;
    }

    /**
     * Returns a list that contains the items that are checked while loading and resetting this page.
     */
    private List<String> initializeCheckedNatures() {
        Set<String> projectNatureIds = new HashSet<String>(projectNatures);
        IProjectNatureDescriptor[] natureDescriptors = project.getWorkspace().getNatureDescriptors();
        List<String> checked = new ArrayList<String>(projectNatureIds.size());
        for (int i = 0; i < natureDescriptors.length; i++) {
            if (projectNatureIds.remove(natureDescriptors[i].getNatureId())) {
                checked.add(fixNatureId(natureDescriptors[i].getNatureId()));
            }
        }
        for (Iterator<String> stream = projectNatureIds.iterator(); stream.hasNext();) {
            checked.add(stream.next());
        }
        return checked;
    }

    private Button createButton(String text, Composite parent, SelectionListener selectionListener) {
        Button b = new Button(parent, SWT.PUSH);
        b.setText(text);
        GridData data = new GridData(GridData.FILL);
        data.widthHint = getButtonWidthHint(b);
        b.setLayoutData(data);
        if (selectionListener != null) {
            b.addSelectionListener(selectionListener);
        }
        return b;
    }

    /*
     * Updates the buttons enablement.
     */
    private void updateButtons() {
        StructuredSelection selection = (StructuredSelection) listViewer.getSelection();
        makePrimaryButton.setEnabled(!selection.isEmpty() && !isPrimary(selection.getFirstElement()));
        restoreButton.setEnabled(modified || isPrimaryModified());
    }

    /*
     * Returns a width hint for a button control.
     */
    private static int getButtonWidthHint(Button button) {
        button.setFont(JFaceResources.getDialogFont());
        PixelConverter converter = new PixelConverter(button);
        int widthHint = converter.convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
        return Math.max(widthHint, button.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x);
    }

    /**
     * Get the defualt widget height for the supplied control.
     * 
     * @return int
     * @param control
     *            - the control being queried about fonts
     * @param lines
     *            - the number of lines to be shown on the table.
     */
    private static int getDefaultFontHeight(Control control, int lines) {
        FontData[] viewerFontData = control.getFont().getFontData();
        int fontHeight = 10;

        // If we have no font data use our guess
        if (viewerFontData.length > 0) {
            fontHeight = viewerFontData[0].getHeight();
        }
        return lines * fontHeight;
    }

    /**
     * Returns a content provider for the list dialog. It will return all available natures as strings.
     * 
     * @return the content provider that shows the natures (as string children)
     */
    private IStructuredContentProvider getContentProvider(final IProject project) {
        return new BaseWorkbenchContentProvider() {
            public Object[] getChildren(Object o) {
                if (!(o instanceof IWorkspace)) {
                    return new Object[0];
                }
                Set<String> projectNatureIds = new HashSet<String>(projectNatures);
                // collect all the natures
                IProjectNatureDescriptor[] natureDescriptors = ((IWorkspace) o).getNatureDescriptors();
                HashSet<String> elements = new HashSet<String>(natureDescriptors.length);
                for (int i = 0; i < natureDescriptors.length; i++) {
                    String natureId = fixNatureId(natureDescriptors[i].getNatureId());
                    if (natureId != null) {
                        if (natureId.startsWith(APTANA_NATURE_PREFIX) || projectNatureIds.contains(natureId)
                                || !showAptanaOnly) {
                            elements.add(natureId);
                            descriptionCache.put(natureId, natureDescriptors[i].getLabel());
                        }
                    }
                }
                // Add any natures that exists in the project, but do not exist in the workbench
                // (This can happen when importing a project from a different workspace, or when the nature
                // provider uninstalled)
                for (String nature : projectNatures) {
                    if (!elements.contains(nature)) {
                        elements.add(nature);
                        descriptionCache.put(nature, EPLMessages.ProjectNaturesPage_MissingDescription);
                    }
                }
                return elements.toArray();
            }
        };
    }

    private ILabelProvider getLabelProvider() {
        return new NaturesLabelProvider();
    }

    protected String getNatureDescriptorLabel(IProjectNatureDescriptor natureDescriptor) {
        String id = natureDescriptor.getNatureId();
        id = fixNatureId(id);
        String label = natureDescriptor.getLabel();
        if (label.trim().length() != 0)
            return id + " (" + label + ')'; //$NON-NLS-1$
        return id;
    }

    /**
     * Creates the table menu.
     * 
     * @return A newly created menu
     */
    protected Menu createMenu() {
        final Menu menu = new Menu(listViewer.getTable());
        MenuItem item = new MenuItem(menu, SWT.PUSH);
        item.setText(EPLMessages.ProjectNaturesPage_LBL_SetPrimary);
        item.addSelectionListener(new MakePrimarySelectionListener());
        return menu;
    }

    private ViewerComparator getViewerComperator() {
        return new ViewerComparator(new Comparator() {
            public int compare(Object element1, Object element2) {
                // set the Aptana on top and the rest on the bottom
                String firstId = element1.toString();
                String secondId = element2.toString();
                if (firstId.startsWith(APTANA_NATURE_PREFIX)) {
                    if (secondId.startsWith(APTANA_NATURE_PREFIX)) {
                        // Compare 2 aptana natures by name
                        return firstId.compareTo(secondId);
                    } else {
                        // Put aptana as first
                        return -1;
                    }
                } else if (secondId.startsWith(APTANA_NATURE_PREFIX)) {
                    // put aptana as first
                    return 1;
                }
                // The natures does not belong to Aptana, so return a simple string comparison
                return firstId.compareTo(secondId);
            }
        });
    }

    /**
     * Handle the exception thrown when saving.
     * 
     * @param e
     *            the exception
     */
    protected void handle(InvocationTargetException e) {
        IdeLog.logError(Activator.getDefault(), EPLMessages.ProjectNaturesPage_ERR_NaturePage, e);
        IStatus error;
        Throwable target = e.getTargetException();
        if (target instanceof CoreException) {
            error = ((CoreException) target).getStatus();
        } else {
            String msg = target.getMessage();
            if (msg == null) {
                msg = IDEWorkbenchMessages.Internal_error;
            }
            error = new Status(IStatus.ERROR, IDEWorkbenchPlugin.IDE_WORKBENCH, 1, msg, target);
        }
        ErrorDialog.openError(getControl().getShell(), null, null, error);
    }

    /**
     * Initializes a ProjectReferencePage.
     */
    private void initialize() {
        project = (IProject) getElement().getAdapter(IResource.class);
        try {
            String[] natureIds = project.getDescription().getNatureIds();
            projectNatures = new ArrayList<String>(Arrays.asList(natureIds));
        } catch (CoreException e) {
            handle(new InvocationTargetException(e));
        }
        primaryNature = (projectNatures != null && !projectNatures.isEmpty()) ? projectNatures.get(0) : null;
        noDefaultAndApplyButton();
        String desc = NLS.bind(EPLMessages.ProjectNaturesPage_TXT_AdditionalNatures, project.getName());
        setDescription(desc);
    }

    /**
     * Returns true if the given element string is set as the primary nature.
     * 
     * @param element
     * @return
     */
    protected boolean isPrimary(Object element) {
        return primaryNature != null && primaryNature.equals(element);
    }

    /**
     * @see org.eclipse.jface.viewers.ICheckStateListener#checkStateChanged(org.eclipse.jface.viewers.CheckStateChangedEvent)
     */
    public void checkStateChanged(CheckStateChangedEvent event) {
        if (maintainChecked.containsKey((event.getElement()))) {
            // revert the change
            maintainChecked.get(event.getElement()).setChecked(!event.getChecked());
        }
        // Check if the current checked items are the same as the initial ones.
        Object[] checkedElements = listViewer.getCheckedElements();
        Arrays.sort(checkedElements);
        modified = !Arrays.equals(initialCheckedItems.toArray(), checkedElements);
        if (primaryNature == null) {
            // in case that the item was checked, set it as the primary
            if (event.getChecked()) {
                primaryNature = event.getElement().toString();
                listViewer.refresh();
            }
        } else {
            if (!event.getChecked() && isPrimary(event.getElement())) {
                // reset the primary element
                primaryNature = null;
                // Find the next available item which is checked and set it to the primary
                checkedElements = listViewer.getCheckedElements();
                if (checkedElements.length > 0) {
                    // take the first checked and set it to defualt
                    primaryNature = checkedElements[0].toString();
                }
                listViewer.refresh();
            }
        }
        updateButtons();
    }

    /**
     * @see PreferencePage#performOk
     */
    public boolean performOk() {
        if (!modified && !isPrimaryModified()) {
            return true;
        }
        // get checked natures
        Object[] checked = listViewer.getCheckedElements();
        final ArrayList<String> natureIds = new ArrayList<String>();
        for (int i = 0; i < checked.length; i++) {
            if (checked[i] instanceof String) {
                natureIds.add(checked[i].toString());
            } else {
                handle(new InvocationTargetException(
                        new IllegalStateException(NLS.bind("invalid element \"{0}\" in nature list", checked[i])))); //$NON-NLS-1$
                return false;
            }
        }
        // Locate and promote the primary item to the top of the list
        natureIds.remove(primaryNature);
        if (primaryNature != null) {
            natureIds.add(0, primaryNature);
        }

        // set nature ids
        IRunnableWithProgress runnable = new IRunnableWithProgress() {
            public void run(IProgressMonitor monitor) throws InvocationTargetException {
                try {
                    IProjectDescription description = project.getDescription();
                    description.setNatureIds(natureIds.toArray(new String[natureIds.size()]));
                    // Use IResource.AVOID_NATURE_CONFIG to avoid any warning about the natures.
                    // We have to use it since not all of the Natures that are defined in the system
                    // are valid and some are forced into the project in a non-standard way.
                    project.setDescription(description, IResource.AVOID_NATURE_CONFIG, monitor);
                } catch (CoreException e) {
                    throw new InvocationTargetException(e);
                }
            }
        };
        try {
            // This will block until the progress is done
            new ProgressMonitorJobsDialog(getControl().getShell()).run(true, true, runnable);
        } catch (InterruptedException e) {
            // Ignore interrupted exceptions
        } catch (InvocationTargetException e) {
            handle(e);
            return false;
        }
        resetProject();
        return true;
    }

    /*
     * Returns true only if the primary nature was modified.
     */
    private boolean isPrimaryModified() {
        // Check if just the primary was modified before exiting
        if (projectNatures != null && !projectNatures.isEmpty()) {
            if (!projectNatures.get(0).equals(primaryNature)) {
                return true;
            }
        } else if (primaryNature != null) {
            return true;
        }
        return false;
    }

    private String fixNatureId(String natureId) {
        int secondComIndex = natureId.indexOf(".com."); //$NON-NLS-1$
        if (secondComIndex > -1) {
            natureId = natureId.substring(secondComIndex + 1);
        }
        return natureId;
    }

    /**
     * Ask to reset the project (e.g. Close and Open) to apply the changes.
     */
    protected void resetProject() {
        boolean reset = MessageDialog.openQuestion(getControl().getShell(),
                EPLMessages.ProjectNaturesPage_ResetTitle, EPLMessages.ProjectNaturesPage_ResetMessage);
        if (reset) {
            IRunnableWithProgress close = new IRunnableWithProgress() {
                public void run(final IProgressMonitor monitor) throws InvocationTargetException {
                    // Use the CloseResourceAction to provide a file saving dialog in case the project has some unsaved
                    // files
                    UIJob job = new UIJob(EPLMessages.ProjectNaturesPage_Job_CloseProject) {
                        public IStatus runInUIThread(IProgressMonitor monitor) {
                            CloseResourceAction closeAction = new CloseResourceAction(
                                    Display.getDefault().getActiveShell());
                            closeAction.selectionChanged(new StructuredSelection(new Object[] { project }));
                            closeAction.run();
                            monitor.done();
                            return Status.OK_STATUS;
                        }
                    };
                    job.schedule();
                    try {
                        job.join();
                    } catch (InterruptedException e) {
                        IdeLog.logError(Activator.getDefault(), EPLMessages.ProjectNaturesPage_ERR_CloseProject, e);
                    }
                    monitor.done();
                }
            };
            try {
                // This will block until the progress is done
                new ProgressMonitorJobsDialog(getControl().getShell()).run(true, true, close);
            } catch (InterruptedException e) {
                // Ignore interrupted exceptions
            } catch (InvocationTargetException e) {
                handle(e);
            }

            IRunnableWithProgress open = new IRunnableWithProgress() {
                public void run(IProgressMonitor monitor) throws InvocationTargetException {
                    try {
                        project.open(monitor);
                    } catch (CoreException e) {
                        throw new InvocationTargetException(e);
                    }
                }
            };
            try {
                // This will block until the progress is done
                new ProgressMonitorJobsDialog(getControl().getShell()).run(true, true, open);
            } catch (InterruptedException e) {
                // Ignore interrupted exceptions
            } catch (InvocationTargetException e) {
                handle(e);
            }
        }
    }

    /**
     * Natures label provider
     */
    private class NaturesLabelProvider extends LabelProvider implements IFontProvider {
        public String getText(Object element) {
            if (element instanceof String) {
                if (descriptionCache.containsKey(element)) {
                    String desc = descriptionCache.get(element);
                    if (isPrimary(element)) {
                        desc += EPLMessages.ProjectNaturesPage_TXT_Primary;
                    }
                    return desc;
                }
                return element.toString();
            }
            if (element instanceof IWorkspace) {
                return "Unknown"; //$NON-NLS-1$
            }
            return super.getText(element);
        }

        public Image getImage(Object element) {
            String natureId = element.toString();
            if (natureId != null && natureId.startsWith(APTANA_NATURE_PREFIX))
                return aptanaNatureImage;
            return super.getImage(element);
        }

        public Font getFont(Object element) {
            updateEnablement(element);
            if (isPrimary(element)) {
                return JFaceResources.getFontRegistry().getBold(JFaceResources.DIALOG_FONT);
            }
            return null;
        }

        private void updateEnablement(Object element) {
            if (maintainChecked.containsKey(element)) {
                return;
            }
            TableItem[] items = listViewer.getTable().getItems();
            for (TableItem item : items) {
                if (item.getData() == element) {
                    if (!item.getData().toString().startsWith(APTANA_NATURE_PREFIX)) {
                        item.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_GRAY));
                        maintainChecked.put(element, item);
                    }
                    break;
                }
            }
        }
    }

    /*
     * A selection adapter that handles the 'Make Primary' clicks.
     */
    private class MakePrimarySelectionListener extends SelectionAdapter {
        public void widgetSelected(SelectionEvent e) {
            ISelection selection = listViewer.getSelection();
            if (!selection.isEmpty() && selection instanceof StructuredSelection) {
                Object firstElement = ((StructuredSelection) selection).getFirstElement();
                // Select the item
                listViewer.setChecked(firstElement, true);
                // Make it as primary
                primaryNature = firstElement.toString();
                listViewer.refresh();
                updateButtons();
            }
        }
    }

    /*
     * A selection adapter that handles the 'Restore' clicks.
     */
    private class RestoreSelectionListener extends SelectionAdapter {
        public void widgetSelected(SelectionEvent e) {
            modified = false;
            initialize();
            listViewer.setCheckedElements(initialCheckedItems.toArray());
            listViewer.refresh();
            updateButtons();
        }
    }
}