org.eclipse.ui.internal.registry.PerspectiveRegistry.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.ui.internal.registry.PerspectiveRegistry.java

Source

/*******************************************************************************
 * Copyright (c) 2000, 2009 IBM Corporation 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
 *     Jan-Hendrik Diederich, Bredex GmbH - bug 201052
 *******************************************************************************/
package org.eclipse.ui.internal.registry;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.dynamichelpers.IExtensionChangeHandler;
import org.eclipse.core.runtime.dynamichelpers.IExtensionTracker;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.StringConverter;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.osgi.util.NLS;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IPerspectiveDescriptor;
import org.eclipse.ui.IPerspectiveRegistry;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPreferenceConstants;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.WorkbenchException;
import org.eclipse.ui.XMLMemento;
import org.eclipse.ui.activities.WorkbenchActivityHelper;
import org.eclipse.ui.internal.IPreferenceConstants;
import org.eclipse.ui.internal.Workbench;
import org.eclipse.ui.internal.WorkbenchMessages;
import org.eclipse.ui.internal.WorkbenchPage;
import org.eclipse.ui.internal.WorkbenchPlugin;
import org.eclipse.ui.internal.handlers.ClosePerspectiveHandler;
import org.eclipse.ui.internal.util.PrefUtil;
import org.eclipse.ui.statushandlers.IStatusAdapterConstants;
import org.eclipse.ui.statushandlers.StatusAdapter;
import org.eclipse.ui.statushandlers.StatusManager;

/**
 * Perspective registry.
 */
public class PerspectiveRegistry implements IPerspectiveRegistry, IExtensionChangeHandler {

    private String defaultPerspID;

    private static final String EXT = "_persp.xml"; //$NON-NLS-1$

    private static final String ID_DEF_PERSP = "PerspectiveRegistry.DEFAULT_PERSP"; //$NON-NLS-1$

    private static final String PERSP = "_persp"; //$NON-NLS-1$

    private static final char SPACE_DELIMITER = ' ';

    private List perspectives = new ArrayList(10);

    // keep track of the perspectives the user has selected to remove or revert
    private ArrayList perspToRemove = new ArrayList(5);

    private IPropertyChangeListener preferenceListener;

    /**
     * Construct a new registry.
     */
    public PerspectiveRegistry() {
        IExtensionTracker tracker = PlatformUI.getWorkbench().getExtensionTracker();
        tracker.registerHandler(this, null);

        initializePreferenceChangeListener();
        WorkbenchPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(preferenceListener);

    }

    /**
     * Initialize the preference change listener.
     */
    private void initializePreferenceChangeListener() {
        preferenceListener = new IPropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent event) {
                /*
                 * To ensure the that no custom perspective definitions are
                 * deleted when preferences are imported, merge old and new
                 * values
                 */
                if (event.getProperty().endsWith(PERSP)) {
                    /* A Perspective is being changed, merge */
                    mergePerspectives(event);
                } else if (event.getProperty().equals(IPreferenceConstants.PERSPECTIVES)) {
                    /* The list of perpsectives is being changed, merge */
                    updatePreferenceList((IPreferenceStore) event.getSource());
                }
            }

            private void mergePerspectives(PropertyChangeEvent event) {
                IPreferenceStore store = (IPreferenceStore) event.getSource();
                if (event.getNewValue() == null || event.getNewValue().equals("")) { //$NON-NLS-1$
                    /*
                     * Perpsective is being removed; if the user has deleted or
                     * reverted a custom perspective, let the change pass
                     * through. Otherwise, restore the custom perspective entry
                     */

                    // Find the matching descriptor in the registry
                    IPerspectiveDescriptor[] perspectiveList = getPerspectives();
                    for (int i = 0; i < perspectiveList.length; i++) {
                        String id = perspectiveList[i].getId();
                        if (event.getProperty().equals(id + PERSP)) { // found
                            // descriptor
                            // see if the perspective has been flagged for
                            // reverting or deleting
                            if (!perspToRemove.contains(id)) { // restore
                                store.setValue(id + PERSP, (String) event.getOldValue());
                            } else { // remove element from the list
                                perspToRemove.remove(id);
                            }
                        }
                    }
                } else if ((event.getOldValue() == null || event.getOldValue().equals(""))) { //$NON-NLS-1$

                    /*
                     * New perspective is being added, update the
                     * perspectiveRegistry to contain the new custom perspective
                     */

                    String id = event.getProperty().substring(0, event.getProperty().lastIndexOf(PERSP));
                    if (findPerspectiveWithId(id) == null) {
                        // perspective does not already exist in registry, add
                        // it
                        PerspectiveDescriptor desc = new PerspectiveDescriptor(null, null, null);
                        StringReader reader = new StringReader((String) event.getNewValue());
                        try {
                            XMLMemento memento = XMLMemento.createReadRoot(reader);
                            desc.restoreState(memento);
                            addPerspective(desc);
                        } catch (WorkbenchException e) {
                            unableToLoadPerspective(e.getStatus());
                        }
                    }
                }
                /* If necessary, add to the list of perspectives */
                updatePreferenceList(store);
            }

            /*
             * Update the list of perspectives from the registry. This will be
             * called for each perspective during an import preferences, but is
             * necessary to ensure the perspectives list stays consistent with
             * the registry
             */
            private void updatePreferenceList(IPreferenceStore store) {
                IPerspectiveDescriptor[] perspectiveList = getPerspectives();
                StringBuffer perspBuffer = new StringBuffer();
                for (int i = 0; i < perspectiveList.length; i++) {
                    PerspectiveDescriptor desc = (PerspectiveDescriptor) perspectiveList[i];
                    if (hasCustomDefinition(desc)) {
                        perspBuffer.append(desc.getId()).append(SPACE_DELIMITER);
                    }
                }
                String newList = perspBuffer.toString().trim();
                store.setValue(IPreferenceConstants.PERSPECTIVES, newList);
            }
        };
    }

    /**
     * Adds a perspective. This is typically used by the reader.
     * 
     * @param desc
     */
    public void addPerspective(PerspectiveDescriptor desc) {
        if (desc == null) {
            return;
        }
        add(desc);
    }

    /**
     * @param desc
     */
    private void add(PerspectiveDescriptor desc) {
        perspectives.add(desc);
        IConfigurationElement element = desc.getConfigElement();
        if (element != null) {
            PlatformUI.getWorkbench().getExtensionTracker().registerObject(element.getDeclaringExtension(), desc,
                    IExtensionTracker.REF_WEAK);
        }
    }

    /**
     * Create a new perspective.
     * 
     * @param label
     *            the name of the new descriptor
     * @param originalDescriptor
     *            the descriptor on which to base the new descriptor
     * @return a new perspective descriptor or <code>null</code> if the
     *         creation failed.
     */
    public PerspectiveDescriptor createPerspective(String label, PerspectiveDescriptor originalDescriptor) {
        // Sanity check to avoid invalid or duplicate labels.
        if (!validateLabel(label)) {
            return null;
        }
        if (findPerspectiveWithLabel(label) != null) {
            return null;
        }

        // Calculate ID.
        String id = label.replace(' ', '_');
        id = id.trim();

        // Create descriptor.
        PerspectiveDescriptor desc = new PerspectiveDescriptor(id, label, originalDescriptor);
        add(desc);
        return desc;
    }

    /**
     * Reverts a list of perspectives back to the plugin definition
     * 
     * @param perspToRevert
     */
    public void revertPerspectives(ArrayList perspToRevert) {
        // indicate that the user is removing these perspectives
        for (int i = 0; i < perspToRevert.size(); i++) {
            PerspectiveDescriptor desc = (PerspectiveDescriptor) perspToRevert.get(i);
            perspToRemove.add(desc.getId());
            desc.revertToPredefined();
        }
    }

    /**
     * Deletes a list of perspectives
     * 
     * @param perspToDelete
     */
    public void deletePerspectives(ArrayList perspToDelete) {
        for (int i = 0; i < perspToDelete.size(); i++) {
            deletePerspective((IPerspectiveDescriptor) perspToDelete.get(i));
        }
    }

    /**
     * Delete a perspective. Has no effect if the perspective is defined in an
     * extension.
     * 
     * @param in
     */
    public void deletePerspective(IPerspectiveDescriptor in) {
        PerspectiveDescriptor desc = (PerspectiveDescriptor) in;
        // Don't delete predefined perspectives
        if (!desc.isPredefined()) {
            perspToRemove.add(desc.getId());
            perspectives.remove(desc);
            desc.deleteCustomDefinition();
            verifyDefaultPerspective();
        }
    }

    /**
     * Delete a perspective. This will remove perspectives defined in
     * extensions.
     * 
     * @param desc
     *            the perspective to delete
     */
    private void internalDeletePerspective(PerspectiveDescriptor desc) {
        perspToRemove.add(desc.getId());
        perspectives.remove(desc);
        desc.deleteCustomDefinition();
        verifyDefaultPerspective();
    }

    /**
     * Removes the custom definition of a perspective from the preference store
     * 
     * @param desc
     */
    /* package */
    void deleteCustomDefinition(PerspectiveDescriptor desc) {
        // remove the entry from the preference store.
        IPreferenceStore store = WorkbenchPlugin.getDefault().getPreferenceStore();

        /*
         * To delete the perspective definition from the preference store, use
         * the setToDefault method. Since no default is defined, this will
         * remove the entry
         */
        store.setToDefault(desc.getId() + PERSP);

    }

    /**
     * Method hasCustomDefinition.
     * 
     * @param desc
     */
    /* package */
    boolean hasCustomDefinition(PerspectiveDescriptor desc) {
        IPreferenceStore store = WorkbenchPlugin.getDefault().getPreferenceStore();
        return store.contains(desc.getId() + PERSP);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ui.IPerspectiveRegistry#findPerspectiveWithId(java.lang.String)
     */
    public IPerspectiveDescriptor findPerspectiveWithId(String id) {
        for (Iterator i = perspectives.iterator(); i.hasNext();) {
            PerspectiveDescriptor desc = (PerspectiveDescriptor) i.next();
            if (desc.getId().equals(id)) {
                if (WorkbenchActivityHelper.restrictUseOf(desc)) {
                    return null;
                }
                return desc;
            }
        }

        return null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ui.IPerspectiveRegistry#findPerspectiveWithLabel(java.lang.String)
     */
    public IPerspectiveDescriptor findPerspectiveWithLabel(String label) {
        for (Iterator i = perspectives.iterator(); i.hasNext();) {
            PerspectiveDescriptor desc = (PerspectiveDescriptor) i.next();
            if (desc.getLabel().equals(label)) {
                if (WorkbenchActivityHelper.restrictUseOf(desc)) {
                    return null;
                }
                return desc;
            }
        }
        return null;
    }

    /**
     * @see IPerspectiveRegistry#getDefaultPerspective()
     */
    public String getDefaultPerspective() {
        return defaultPerspID;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ui.IPerspectiveRegistry#getPerspectives()
     */
    public IPerspectiveDescriptor[] getPerspectives() {
        Collection descs = WorkbenchActivityHelper.restrictCollection(perspectives, new ArrayList());
        return (IPerspectiveDescriptor[]) descs.toArray(new IPerspectiveDescriptor[descs.size()]);
    }

    /**
     * Loads the registry.
     */
    public void load() {
        // Load the registries.
        loadPredefined();
        loadCustom();

        // Get default perspective.
        // Get it from the R1.0 dialog settings first. Fixes bug 17039
        IDialogSettings dialogSettings = WorkbenchPlugin.getDefault().getDialogSettings();
        String str = dialogSettings.get(ID_DEF_PERSP);
        if (str != null && str.length() > 0) {
            setDefaultPerspective(str);
            dialogSettings.put(ID_DEF_PERSP, ""); //$NON-NLS-1$
        }
        verifyDefaultPerspective();
    }

    /**
     * Read children from the file system.
     */
    private void loadCustom() {
        Reader reader = null;

        /* Get the entries from the Preference store */
        IPreferenceStore store = WorkbenchPlugin.getDefault().getPreferenceStore();

        /* Get the space-delimited list of custom perspective ids */
        String customPerspectives = store.getString(IPreferenceConstants.PERSPECTIVES);
        String[] perspectivesList = StringConverter.asArray(customPerspectives);

        for (int i = 0; i < perspectivesList.length; i++) {
            try {
                String xmlString = store.getString(perspectivesList[i] + PERSP);
                if (xmlString != null && xmlString.length() != 0) {
                    reader = new StringReader(xmlString);
                } else {
                    throw new WorkbenchException(new Status(IStatus.ERROR, WorkbenchPlugin.PI_WORKBENCH,
                            NLS.bind(WorkbenchMessages.get().Perspective_couldNotBeFound, perspectivesList[i])));
                }

                // Restore the layout state.
                XMLMemento memento = XMLMemento.createReadRoot(reader);
                PerspectiveDescriptor newPersp = new PerspectiveDescriptor(null, null, null);
                newPersp.restoreState(memento);
                String id = newPersp.getId();
                IPerspectiveDescriptor oldPersp = findPerspectiveWithId(id);
                if (oldPersp == null) {
                    add(newPersp);
                }
                reader.close();
            } catch (IOException e) {
                unableToLoadPerspective(null);
            } catch (WorkbenchException e) {
                unableToLoadPerspective(e.getStatus());
            }
        }

        // Get the entries from files, if any
        // if -data @noDefault specified the state location may not be
        // initialized
        IPath path = WorkbenchPlugin.getDefault().getDataLocation();
        if (path == null) {
            return;
        }

        File folder = path.toFile();

        if (folder.isDirectory()) {
            File[] fileList = folder.listFiles();
            int nSize = fileList.length;
            for (int nX = 0; nX < nSize; nX++) {
                File file = fileList[nX];
                if (file.getName().endsWith(EXT)) {
                    // get the memento
                    InputStream stream = null;
                    try {
                        stream = new FileInputStream(file);
                        reader = new BufferedReader(new InputStreamReader(stream, "utf-8")); //$NON-NLS-1$

                        // Restore the layout state.
                        XMLMemento memento = XMLMemento.createReadRoot(reader);
                        PerspectiveDescriptor newPersp = new PerspectiveDescriptor(null, null, null);
                        newPersp.restoreState(memento);
                        IPerspectiveDescriptor oldPersp = findPerspectiveWithId(newPersp.getId());
                        if (oldPersp == null) {
                            add(newPersp);
                        }

                        // save to the preference store
                        saveCustomPersp(newPersp, memento);

                        // delete the file
                        file.delete();

                        reader.close();
                        stream.close();
                    } catch (IOException e) {
                        unableToLoadPerspective(null);
                    } catch (WorkbenchException e) {
                        unableToLoadPerspective(e.getStatus());
                    }
                }
            }
        }
    }

    /**
     * @param status
     */
    private void unableToLoadPerspective(IStatus status) {
        String msg = "Unable to load perspective."; //$NON-NLS-1$
        if (status == null) {
            IStatus errStatus = new Status(IStatus.ERROR, WorkbenchPlugin.PI_WORKBENCH, msg);
            StatusManager.getManager().handle(errStatus, StatusManager.SHOW | StatusManager.LOG);
        } else {
            StatusAdapter adapter = new StatusAdapter(status);
            adapter.setProperty(IStatusAdapterConstants.TITLE_PROPERTY, msg);
            StatusManager.getManager().handle(adapter, StatusManager.SHOW | StatusManager.LOG);
        }
    }

    /**
     * Saves a custom perspective definition to the preference store.
     * 
     * @param desc
     *            the perspective
     * @param memento
     *            the memento to save to
     * @throws IOException
     */
    public void saveCustomPersp(PerspectiveDescriptor desc, XMLMemento memento) throws IOException {

        IPreferenceStore store = WorkbenchPlugin.getDefault().getPreferenceStore();

        // Save it to the preference store.
        Writer writer = new StringWriter();

        memento.save(writer);
        writer.close();
        store.setValue(desc.getId() + PERSP, writer.toString());

    }

    /**
     * Gets the Custom perspective definition from the preference store.
     * 
     * @param id
     *            the id of the perspective to find
     * @return IMemento a memento containing the perspective description
     * 
     * @throws WorkbenchException
     * @throws IOException
     */
    public IMemento getCustomPersp(String id) throws WorkbenchException, IOException {
        Reader reader = null;

        IPreferenceStore store = WorkbenchPlugin.getDefault().getPreferenceStore();
        String xmlString = store.getString(id + PERSP);
        if (xmlString != null && xmlString.length() != 0) { // defined in store
            reader = new StringReader(xmlString);
        } else {
            throw new WorkbenchException(new Status(IStatus.ERROR, WorkbenchPlugin.PI_WORKBENCH,
                    NLS.bind(WorkbenchMessages.get().Perspective_couldNotBeFound, id)));
        }
        XMLMemento memento = XMLMemento.createReadRoot(reader);
        reader.close();
        return memento;
    }

    /**
     * Read children from the plugin registry.
     */
    private void loadPredefined() {
        PerspectiveRegistryReader reader = new PerspectiveRegistryReader(this);
        reader.readPerspectives(Platform.getExtensionRegistry());
    }

    /**
     * @see IPerspectiveRegistry#setDefaultPerspective(String)
     */
    public void setDefaultPerspective(String id) {
        IPerspectiveDescriptor desc = findPerspectiveWithId(id);
        if (desc != null) {
            defaultPerspID = id;
            PrefUtil.getAPIPreferenceStore().setValue(IWorkbenchPreferenceConstants.DEFAULT_PERSPECTIVE_ID, id);
        }
    }

    /**
     * Return <code>true</code> if a label is valid. This checks only the
     * given label in isolation. It does not check whether the given label is
     * used by any existing perspectives.
     * 
     * @param label
     *            the label to test
     * @return whether the label is valid
     */
    public boolean validateLabel(String label) {
        label = label.trim();
        if (label.length() <= 0) {
            return false;
        }
        return true;
    }

    /**
     * Verifies the id of the default perspective. If the default perspective is
     * invalid use the workbench default.
     */
    private void verifyDefaultPerspective() {
        // Step 1: Try current defPerspId value.
        IPerspectiveDescriptor desc = null;
        if (defaultPerspID != null) {
            desc = findPerspectiveWithId(defaultPerspID);
        }
        if (desc != null) {
            return;
        }

        // Step 2. Read default value.
        String str = PrefUtil.getAPIPreferenceStore()
                .getString(IWorkbenchPreferenceConstants.DEFAULT_PERSPECTIVE_ID);
        if (str != null && str.length() > 0) {
            desc = findPerspectiveWithId(str);
        }
        if (desc != null) {
            defaultPerspID = str;
            return;
        }

        // Step 3. Use application-specific default
        Workbench instance = Workbench.getInstance();
        if (instance != null) {
            defaultPerspID = instance.getDefaultPerspectiveId();
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ui.IPerspectiveRegistry#clonePerspective(java.lang.String,
     *      java.lang.String, org.eclipse.ui.IPerspectiveDescriptor)
     */
    public IPerspectiveDescriptor clonePerspective(String id, String label,
            IPerspectiveDescriptor originalDescriptor) {

        // Check for invalid labels
        if (label == null || !(label.trim().length() > 0)) {
            throw new IllegalArgumentException();
        }

        // Check for duplicates
        IPerspectiveDescriptor desc = findPerspectiveWithId(id);
        if (desc != null) {
            throw new IllegalArgumentException();
        }

        // Create descriptor.
        desc = new PerspectiveDescriptor(id, label, (PerspectiveDescriptor) originalDescriptor);
        add((PerspectiveDescriptor) desc);
        return desc;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ui.IPerspectiveRegistry#revertPerspective(org.eclipse.ui.IPerspectiveDescriptor)
     */
    public void revertPerspective(IPerspectiveDescriptor perspToRevert) {
        PerspectiveDescriptor desc = (PerspectiveDescriptor) perspToRevert;
        perspToRemove.add(desc.getId());
        desc.revertToPredefined();
    }

    /**
     * Dispose the receiver.
     */
    public void dispose() {
        PlatformUI.getWorkbench().getExtensionTracker().unregisterHandler(this);
        WorkbenchPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(preferenceListener);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.core.runtime.dynamicHelpers.IExtensionChangeHandler#removeExtension(org.eclipse.core.runtime.IExtension,
     *      java.lang.Object[])
     */
    public void removeExtension(IExtension source, Object[] objects) {
        for (int i = 0; i < objects.length; i++) {
            if (objects[i] instanceof PerspectiveDescriptor) {
                // close the perspective in all windows
                IWorkbenchWindow[] windows = PlatformUI.getWorkbench().getWorkbenchWindows();
                PerspectiveDescriptor desc = (PerspectiveDescriptor) objects[i];
                for (int w = 0; w < windows.length; ++w) {
                    IWorkbenchWindow window = windows[w];
                    IWorkbenchPage[] pages = window.getPages();
                    for (int p = 0; p < pages.length; ++p) {
                        WorkbenchPage page = (WorkbenchPage) pages[p];
                        ClosePerspectiveHandler.closePerspective(page, page.findPerspective(desc));
                    }
                }

                // ((Workbench)PlatformUI.getWorkbench()).getPerspectiveHistory().removeItem(desc);

                internalDeletePerspective(desc);
            }

        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.core.runtime.dynamicHelpers.IExtensionChangeHandler#addExtension(org.eclipse.core.runtime.dynamicHelpers.IExtensionTracker,
     *      org.eclipse.core.runtime.IExtension)
     */
    public void addExtension(IExtensionTracker tracker, IExtension addedExtension) {
        IConfigurationElement[] addedElements = addedExtension.getConfigurationElements();
        for (int i = 0; i < addedElements.length; i++) {
            PerspectiveRegistryReader reader = new PerspectiveRegistryReader(this);
            reader.readElement(addedElements[i]);
        }
    }
}