org.eclipse.jst.jsf.facesconfig.ui.pageflow.PageflowEditor.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jst.jsf.facesconfig.ui.pageflow.PageflowEditor.java

Source

/*******************************************************************************
 * Copyright (c) 2004, 2005 Sybase, Inc. 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:
 *     Sybase, Inc. - initial API and implementation
 *******************************************************************************/

package org.eclipse.jst.jsf.facesconfig.ui.pageflow;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.draw2d.PositionConstants;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.gef.ContextMenuProvider;
import org.eclipse.gef.DefaultEditDomain;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPartViewer;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.GraphicalViewer;
import org.eclipse.gef.KeyHandler;
import org.eclipse.gef.KeyStroke;
import org.eclipse.gef.RootEditPart;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.gef.commands.CommandStackEvent;
import org.eclipse.gef.commands.CommandStackEventListener;
import org.eclipse.gef.editparts.ScalableFreeformRootEditPart;
import org.eclipse.gef.editparts.ScalableRootEditPart;
import org.eclipse.gef.editparts.ZoomManager;
import org.eclipse.gef.palette.PaletteRoot;
import org.eclipse.gef.requests.CreationFactory;
import org.eclipse.gef.ui.actions.ActionRegistry;
import org.eclipse.gef.ui.actions.DeleteAction;
import org.eclipse.gef.ui.actions.GEFActionConstants;
import org.eclipse.gef.ui.actions.RedoAction;
import org.eclipse.gef.ui.actions.SelectionAction;
import org.eclipse.gef.ui.actions.StackAction;
import org.eclipse.gef.ui.actions.UndoAction;
import org.eclipse.gef.ui.actions.UpdateAction;
import org.eclipse.gef.ui.actions.ZoomInAction;
import org.eclipse.gef.ui.actions.ZoomOutAction;
import org.eclipse.gef.ui.parts.GraphicalEditorWithFlyoutPalette;
import org.eclipse.gef.ui.parts.GraphicalViewerKeyHandler;
import org.eclipse.gef.ui.parts.ScrollingGraphicalViewer;
import org.eclipse.gef.ui.parts.SelectionSynchronizer;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.resource.FontRegistry;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.util.TransferDropTargetListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jst.jsf.common.ui.internal.logging.Logger;
import org.eclipse.jst.jsf.facesconfig.ui.EditorPlugin;
import org.eclipse.jst.jsf.facesconfig.ui.pageflow.action.AlignmentAction;
import org.eclipse.jst.jsf.facesconfig.ui.pageflow.action.OpenEditorAction;
import org.eclipse.jst.jsf.facesconfig.ui.pageflow.action.ShowPropertyViewAction;
import org.eclipse.jst.jsf.facesconfig.ui.pageflow.command.PreExecuteCommandStack;
import org.eclipse.jst.jsf.facesconfig.ui.pageflow.editpart.ConfigurableRootEditPart;
import org.eclipse.jst.jsf.facesconfig.ui.pageflow.editpart.IConnectionPreference;
import org.eclipse.jst.jsf.facesconfig.ui.pageflow.editpart.IFigurePreference;
import org.eclipse.jst.jsf.facesconfig.ui.pageflow.editpart.ILayerPanePreference;
import org.eclipse.jst.jsf.facesconfig.ui.pageflow.editpart.INodePreference;
import org.eclipse.jst.jsf.facesconfig.ui.pageflow.editpart.PageflowEditPartsFactory;
import org.eclipse.jst.jsf.facesconfig.ui.pageflow.editpart.PageflowNodeEditPart;
import org.eclipse.jst.jsf.facesconfig.ui.pageflow.model.Pageflow;
import org.eclipse.jst.jsf.facesconfig.ui.pageflow.model.PageflowPage;
import org.eclipse.jst.jsf.facesconfig.ui.pageflow.synchronization.FC2PFTransformer;
import org.eclipse.jst.jsf.facesconfig.ui.pageflow.util.EditPartMarkerUtil;
import org.eclipse.jst.jsf.facesconfig.ui.pageflow.util.PageflowAnnotationUtil;
import org.eclipse.jst.jsf.facesconfig.ui.pageflow.util.PageflowModelManager;
import org.eclipse.jst.jsf.facesconfig.ui.pageflow.util.PageflowResourceFactory;
import org.eclipse.jst.jsf.facesconfig.ui.preference.GEMPreferences;
import org.eclipse.jst.jsf.facesconfig.ui.util.WebrootUtil;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.ide.IGotoMarker;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
import org.eclipse.ui.views.properties.IPropertySheetPage;
import org.eclipse.ui.views.properties.PropertySheetPage;

/**
 * This the the main editor page for modifying a complete pageflow.
 */
public class PageflowEditor extends GraphicalEditorWithFlyoutPalette
        implements IAdaptable, IPropertyChangeListener, IGotoMarker {
    /** log instance */
    private static final Logger log = EditorPlugin.getLogger(PageflowEditor.class);

    /** pageflow context menu registration ID */
    private static final String PAGEFLOW_CONTEXTMENU_REG_ID = ".pageflow.editor.contextmenu"; //$NON-NLS-1$

    /** the edit domain */
    private final DefaultEditDomain domain;

    /** the palette root */
    private PaletteRoot paletteRoot = null;

    /** the parent multi-page editor */
    private IEditorPart parentEditor = null;

    /** the graphical viewer */
    private GraphicalViewer viewer = null;

    /** the undoable <code>IPropertySheetPage</code> */
    private PropertySheetPage undoablePropertySheetPage = null;

    /** the editor's action registry */
    private ActionRegistry actionRegistry = null;

    /**
     * The id of the editor page
     */
    public static final String PAGE_ID = "org.eclipse.jst.jsf.facesconfig.ui.pageflow.PageflowEditor"; //$NON-NLS-1$

    /** the list of action ids that are to EditPart actions */
    private List editPartActionIDs = new ArrayList();

    /** the selection listener */
    private ISelectionListener selectionListener = new ISelectionListener() {
        public void selectionChanged(IWorkbenchPart part, ISelection selection) {
            updateActions();
        }
    };

    /** the selection synchronizer for the edit part viewer */
    private SelectionSynchronizer synchronizer = null;

    /** the shared key handler */
    private KeyHandler sharedKeyHandler = null;

    /** pageflow model manager */
    private PageflowModelManager pageflowManager;

    /** the dirty status of this page */
    private boolean isDirty = false;

    /** the command stack of this page */
    private CommandStack commandStack;

    private FC2PFTransformer modelsTransform;

    List stackActions = new ArrayList();

    /**
     * @return the faces-config to pageflow model
     */
    public FC2PFTransformer getModelsTransform() {
        if (modelsTransform == null) {
            modelsTransform = new FC2PFTransformer();
        }
        return modelsTransform;
    }

    /**
     * update the editor actions
     */
    public void updateActions() {
        updateActions(stackActions);
        updateActions(editPartActionIDs);
    }

    /**
     * This class listens for command stack changes of the page and decides if
     * the editor is dirty or not.
     * 
     */
    private class PageCommandStackListener implements CommandStackEventListener {
        public void stackChanged(CommandStackEvent event) {
            if (((CommandStack) event.getSource()).isDirty()) {
                // at least one command stack is dirty,
                // so the multi page editor is dirty too
                setDirty(true);
            }
            updateActions();
        }
    }

    /**
     * This class listens to changes to the file system in the workspace, and
     * validate the current pageflow based on web files' status.
     */
    private class ResourceTracker implements IResourceChangeListener, IResourceDeltaVisitor {
        /*
         * (non-Javadoc)
         * 
         * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent)
         */
        public void resourceChanged(IResourceChangeEvent event) {
            IResourceDelta delta = event.getDelta();
            try {
                if (delta != null) {
                    delta.accept(this);
                }
            } catch (CoreException exception) {
                // Pageflow.PageflowEditor.Error.ResourceChange = Failed in the
                // resource change.
                log.error("Pageflow.PageflowEditor.Error.ResourceChange", //$NON-NLS-1$
                        exception);
            }
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.eclipse.core.resources.IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta)
         */
        public boolean visit(IResourceDelta delta) {
            // if the delta is not a file instance, just return true
            if (!(delta.getResource() instanceof IFile)) {
                return true;
            }

            // web file is changed.
            if (WebrootUtil.isValidWebFile(((IFile) delta.getResource()).getFullPath())) {
                webPageChanged(((IFile) delta.getResource()).getFullPath());
                return false;
            }
            return false;
        }
    }

    /** the resource tracker instance */
    private ResourceTracker resourceTracker = null;

    /**
     * Returns the resource tracker instance
     * 
     * @return - Returns the resource tracker instance
     */
    private ResourceTracker getResourceTracker() {
        if (null == resourceTracker) {
            resourceTracker = new ResourceTracker();
        }
        return resourceTracker;
    }

    /**
     * Changes the dirty state.
     * 
     * @param dirty -
     *            dirty state
     */
    protected void setDirty(boolean dirty) {
        if (isDirty != dirty) {
            isDirty = dirty;
        }
    }

    /**
     * Updates the specified actions.
     * 
     * @param actionIds -
     *            the list of ids of actions to update
     */
    protected void updateActions(List actionIds) {
        for (Iterator ids = actionIds.iterator(); ids.hasNext();) {
            IAction action = getActionRegistry().getAction(ids.next());
            if (null != action && action instanceof UpdateAction) {
                ((UpdateAction) action).update();
            }
        }
    }

    /**
     * Creates a new PageflowPage instance.
     * <p>
     * By design this page uses its own <code>EditDomain</code>. The main
     * goal of this approach is that this page has its own undo/redo command
     * stack.
     * 
     * @param parent -
     *            the parent multi page editor
     */
    public PageflowEditor(IEditorPart parent) {
        domain = new DefaultEditDomain(parent);
        domain.setCommandStack(getCommandStack());
        this.setEditDomain(domain);
        parentEditor = parent;
    }

    /**
     * Adds an <code>CommandStack</code> action to this editor.
     * <p>
     * <code>CommandStack</code> actions are actions that depend and work on
     * the <code>CommandStack</code>.
     * 
     * @param action -
     *            the <code>CommandStack</code> action
     */
    protected void addStackAction(StackAction action) {
        getActionRegistry().registerAction(action);
        stackActions.add(action.getId());
    }

    /**
     * Creates different kinds of actions and registers them to the
     * ActionRegistry.
     */
    protected void createActions() {
        // register delete action
        addEditPartAction(new DeleteAction((IWorkbenchPart) this));
        // register undo/redo action
        addStackAction(new UndoAction(this));
        addStackAction(new RedoAction(this));

        // Allows opening of JSP files from the pageflow
        addEditPartAction(new OpenEditorAction(this));

        // Allows showing property view for the pageflow
        SelectionAction action = new ShowPropertyViewAction(this);
        action.setImageDescriptor(getImageDescriptorForView("org.eclipse.ui.views.PropertySheet")); //$NON-NLS-1$
        addEditPartAction(action);
        // Allows showing property view for the pageflow
        // addEditPartAction(new ShowPaletteViewAction((IWorkbenchPart) this));

        // register alignment actions
        addEditPartAction(new AlignmentAction((IWorkbenchPart) this, PositionConstants.LEFT));
        addEditPartAction(new AlignmentAction((IWorkbenchPart) this, PositionConstants.RIGHT));
        addEditPartAction(new AlignmentAction((IWorkbenchPart) this, PositionConstants.TOP));
        addEditPartAction(new AlignmentAction((IWorkbenchPart) this, PositionConstants.BOTTOM));
        addEditPartAction(new AlignmentAction((IWorkbenchPart) this, PositionConstants.CENTER));
        addEditPartAction(new AlignmentAction((IWorkbenchPart) this, PositionConstants.MIDDLE));

        // register zoom in/out action
        IAction zoomIn = new ZoomInAction(getZoomManager(getGraphicalViewer()));
        IAction zoomOut = new ZoomOutAction(getZoomManager(getGraphicalViewer()));
        addAction(zoomIn);
        addAction(zoomOut);

        //      getSite().getKeyBindingService().registerAction(zoomIn);
        //      getSite().getKeyBindingService().registerAction(zoomOut);
    }

    /**
     * Returns the zoom manager of the specified viewer.
     * 
     * @param viewer_ -
     *            the viewer to get the zoom manager from
     * @return - the zoom manager
     */
    private ZoomManager getZoomManager(GraphicalViewer viewer_) {
        // get zoom manager from root edit part
        RootEditPart rootEditPart = viewer_.getRootEditPart();
        ZoomManager zoomManager = null;
        if (rootEditPart instanceof ScalableFreeformRootEditPart) {
            zoomManager = ((ScalableFreeformRootEditPart) rootEditPart).getZoomManager();
        } else if (rootEditPart instanceof ScalableRootEditPart) {
            zoomManager = ((ScalableRootEditPart) rootEditPart).getZoomManager();
        }
        return zoomManager;
    }

    /**
     * Adds an action to this editor's <code>ActionRegistry</code>.
     * 
     * @param action -
     *            the action to add.
     */
    protected void addAction(IAction action) {
        getActionRegistry().registerAction(action);
    }

    /**
     * Adds an <code>EditPart</code> action to this editor.
     * <p>
     * <code>EditPart</code> actions are actions that depend and work on the
     * selected <code>EditPart</code>s.
     * 
     * @param action -
     *            the <code>EditPart</code> action
     */
    protected void addEditPartAction(SelectionAction action) {
        getActionRegistry().registerAction(action);
        editPartActionIDs.add(action.getId());
    }

    /**
     * Returns the action registry of this editor.
     * 
     * @return - the action registry
     */
    public ActionRegistry getActionRegistry() {
        if (null == actionRegistry) {
            actionRegistry = new ActionRegistry();
        }

        return actionRegistry;
    }

    /**
     * Creates the GraphicalViewer on the specified <code>Composite</code>.
     * 
     * @param parent -
     *            the parent composite
     */
    public void createGraphicalViewer(Composite parent) {
        viewer = new ScrollingGraphicalViewer();
        viewer.createControl(parent);

        // configure the viewer
        viewer.getControl().setBackground(parent.getBackground());

        viewer.setRootEditPart(new ConfigurableRootEditPart());
        // _viewer.setRootEditPart(new ScalableFreeformRootEditPart());
        viewer.setKeyHandler(new GraphicalViewerKeyHandler(viewer));

        // hook the viewer into the editor
        registerEditPartViewer(viewer);

        // configure the viewer with context menu and template drag and drop
        configureEditPartViewer(viewer);

        // initialize the viewer with input
        viewer.setEditPartFactory(new PageflowEditPartsFactory());
        // viewer.setContents(getPageflow());

        // support the resource drag&drop
        viewer.addDropTargetListener(
                (TransferDropTargetListener) new ResourceTransferDropTargetListener(viewer, getParentEditor()) {
                    protected CreationFactory getFactory(Object obj) {
                        return getResourceFactory((IResource) obj);
                    }
                });

        // apply Editor's preferences
        // propertyChange(null);
        // add listener to Editor's preferences changing
        EditorPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(this);
    }

    private CreationFactory getResourceFactory(IResource resource) {
        return new PageflowResourceFactory(resource);
    }

    /*
     * (non-Javadoc)
     * 
     * @see ISaveablePart#doSave(org.eclipse.core.runtime.IProgressMonitor)
     */
    public final void doSave(IProgressMonitor monitor) {
        // our policy: delegate saving to the parent
        getParentEditor().doSave(monitor);
    }

    /*
     * (non-Javadoc)
     * 
     * @see ISaveablePart#doSaveAs()
     */
    public final void doSaveAs() {
        // our policy: delegate saving to the parent
        getParentEditor().doSaveAs();
    }

    /**
     * Saves the pageflow under the specified path.
     * @param file 
     * 
     * @param progressMonitor
     * @throws CoreException 
     */
    public void doSave(IFile file, IProgressMonitor progressMonitor) throws CoreException {
        if (((FileEditorInput) getEditorInput()).getFile() != file) {
            // TODO: save to other page.
        }
        if (null == progressMonitor) {
            progressMonitor = new NullProgressMonitor();
        }
        // Pageflow.Label.Saving = Saving
        progressMonitor.beginTask(PageflowMessages.Pageflow_Label_Saving + " " //$NON-NLS-1$
                + file.getFullPath(), 2);

        if (null == getPageflowManager()) {
            // Pageflow.PageflowEditor.Alert.nullModelManager = No model manager
            // found for saving the file.
            EditorPlugin.getAlerts().throwCoreException("Pageflow.PageflowEditor.Alert.nullModelManager"); //$NON-NLS-1$
        }

        // save pageflow to file
        try {
            //Bug 291054 - faces-config should be checked out if the user tries to modify it
            IPath pageFlowFilePath = getPageflowFilePath(file);
            if (pageFlowFilePath != null) {
                IWorkspace workspace = ResourcesPlugin.getWorkspace();
                IFile pageFlowFile = workspace.getRoot().getFile(pageFlowFilePath);
                if (pageFlowFile != null && pageFlowFile.isReadOnly()) {
                    workspace.validateEdit(new IFile[] { pageFlowFile },
                            getEditorSite() != null ? getEditorSite().getShell() : null);
                }
            }

            getPageflowManager().save(getPageflowFilePath(file));

            progressMonitor.worked(1);
            file.refreshLocal(IResource.DEPTH_ZERO, new SubProgressMonitor(progressMonitor, 1));
            progressMonitor.done();
            setDirty(false);
        } catch (FileNotFoundException e) {
            // Pageflow.PageflowEditor.Alert.errorSaveFileInfo = The current
            // pageflow model could not be saved.
            EditorPlugin.getAlerts().throwCoreException(e);
        } catch (IOException e) {
            // Pageflow.PageflowEditor.Alert.errorSaveFileInfo = The current
            // pageflow model could not be saved.
            EditorPlugin.getAlerts().throwCoreException(e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see IEditorPart#init(org.eclipse.ui.IEditorSite,
     *      org.eclipse.ui.IEditorInput)
     */
    public void init(IEditorSite site, IEditorInput input) throws PartInitException {
        setSite(site);
        setInput(input);
        IFile fileFacesConfig = null;
        try {
            fileFacesConfig = ((FileEditorInput) input).getFile();

            // load and validate pageflow
            if (null == createPageflow(getPageflowFilePath(fileFacesConfig))) {
                // Pageflow.PageflowEditor.Error.invalidPageflowFile = The
                // specified input is not a valid pageflow.
                log.error("Pageflow.PageflowEditor.Error.invalidPageflowFile"); //$NON-NLS-1$
                throw new PartInitException(
                        EditorPlugin.getResourceString("Pageflow.PageflowEditor.Error.invalidPageflowFile")); //$NON-NLS-1$
            }

        } catch (CoreException e) {
            // Pageflow.PageflowEditor.Error.invalidPageflowFile = The specified
            // input is not a valid pageflow.
            log.error("Pageflow.PageflowEditor.Error.invalidPageflowFile", e); //$NON-NLS-1$
            throw new PartInitException(e.getStatus());
        } catch (IOException e) {
            // Pageflow.PageflowEditor.Alert.errorSaveFileInfo = The current
            // pageflow model could not be saved.
            log.error("Pageflow.PageflowEditor.Alert.errorSaveFileInfo", e); //$NON-NLS-1$
        }

        // add selection change listener
        getSite().getWorkbenchWindow().getSelectionService().addSelectionListener(getSelectionListener());

        // Add resource change listener
        fileFacesConfig.getWorkspace().addResourceChangeListener(getResourceTracker());
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ui.IWorkbenchPart#dispose()
     */
    public void dispose() {
        // remove selection change listener
        getModelsTransform().dispose();
        getSite().getWorkbenchWindow().getSelectionService().removeSelectionListener(getSelectionListener());

        // remove listener to Editor's preferences changing
        EditorPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(this);

        if (getEditorInput() != null) {
            IFile file = (IFile) getEditorInput().getAdapter(IResource.class);
            if (file != null) {
                file.getWorkspace().removeResourceChangeListener(getResourceTracker());
            }
        }
        super.dispose();
    }

    /**
     * get the pageflow file path based on faces-config.xml file path
     * 
     * @return
     */
    private IPath getPageflowFilePath(IFile file) {
        IPath pageflowFilePath;
        pageflowFilePath = PageflowModelManager.makePageflowPath(file.getFullPath());
        return pageflowFilePath;
    }

    /**
     * Returns the pageflow object from the specified file.
     * 
     * @param file -
     *            the file resource
     * @return -the pageflow object from the specified file
     * @throws IOException
     */
    private Pageflow createPageflow(IPath pathPageflow) throws CoreException, IOException {
        Pageflow pageflow = null;

        try {
            getPageflowManager().load(pathPageflow);
        } catch (Exception e) {
            // Pageflow.PageflowEditor.Error.invalidPageflowFile = The specified
            // input is not a valid pageflow.
            // _log.error("Pageflow.PageflowEditor.Error.invalidPageflowFile",
            // e);
            getPageflowManager().createPageflow(pathPageflow);
        }
        IFile fileFacesConfig = ((FileEditorInput) getEditorInput()).getFile();
        // it should update related config file
        if (!fileFacesConfig.getFullPath().toString().trim()
                .equalsIgnoreCase(getPageflowManager().getModel().getConfigfile())) {
            getPageflowManager().getModel().setConfigfile(fileFacesConfig.getFullPath().toString());
            getPageflowManager().save(pathPageflow);
        }
        pageflow = getPageflowManager().getModel();
        if (null == pageflow) {
            // Pageflow.PageflowEditor.Error.invalidPageflowModel = The model in
            // the pageflow file is not a valid pageflow model.
            log.error("Pageflow.PageflowEditor.Error.invalidPageflowModel"); //$NON-NLS-1$
            EditorPlugin.getAlerts().throwCoreException("Pageflow.PageflowEditor.Error.invalidPageflowModel"); //$NON-NLS-1$
        }
        return pageflow;
    }

    /** get the pageflow manager for this page 
     * @return  the model manager
     */
    public PageflowModelManager getPageflowManager() {
        if (pageflowManager == null) {
            pageflowManager = new PageflowModelManager();
        }
        return pageflowManager;
    }

    /*
     * (non-Javadoc)
     * 
     * @see ISaveablePart#isDirty()
     */
    public final boolean isDirty() {
        return isDirty;
    }

    /**
     * Returns the <code>CommandStack</code> of this editor page.
     * 
     * @return - the <code>CommandStack</code> of this editor page
     */
    public final CommandStack getCommandStack() {
        if (commandStack == null) {
            commandStack = new PreExecuteCommandStack();
            commandStack.addCommandStackEventListener(new PageCommandStackListener());
        }
        return commandStack;
    }

    /**
     * Returns the default <code>PaletteRoot</code> for this editor and all
     * its pages.
     * 
     * @return - the default <code>PaletteRoot</code>
     */
    protected PaletteRoot getPaletteRoot() {
        if (null == paletteRoot) {
            // create root
            paletteRoot = new PageflowPaletteRoot();
        }
        return paletteRoot;
    }

    /*
     * (non-Javadoc)
     * 
     * @see ISaveablePart#isSaveAsAllowed()
     */
    public final boolean isSaveAsAllowed() {
        // our policy: delegate saving to the parent
        return getParentEditor().isSaveAsAllowed();
    }

    /*
     * (non-Javadoc)
     * 
     * @see IWorkbenchPart#setFocus()
     */
    public void setFocus() {
        getGraphicalViewer().getControl().setFocus();
    }

    /**
     * Returns the multi page pageflow editor this editor page is contained in.
     * 
     * @return - the parent multi page editor
     */
    protected final IEditorPart getParentEditor() {
        return parentEditor;
    }

    /**
     * Returns the edit domain this editor page uses.
     * 
     * @return - the edit domain this editor page uses
     */
    public final DefaultEditDomain getEditDomain() {
        return domain;
    }

    /**
     * Hooks a <code>EditPartViewer</code> to the rest of the Editor.
     * <p>
     * By default, the viewer is added to the SelectionSynchronizer, which can
     * be used to keep 2 or more EditPartViewers in sync. The viewer is also
     * registered as the ISelectionProvider for the Editor's PartSite.
     * 
     * @param viewer_ -
     *            the viewer to hook into the editor
     */
    protected void registerEditPartViewer(EditPartViewer viewer_) {
        // register viewer to edit domain
        getEditDomain().addViewer(viewer_);

        // the multi page pageflow editor keeps track of synchronizing
        getSelectionSynchronizer().addViewer(viewer_);

        // add viewer as selection provider
        getSite().setSelectionProvider(viewer_);
    }

    /**
     * Configures the specified <code>EditPartViewer</code> including context
     * menu, key handler, etc.
     * 
     * @param viewer_ -
     *            the pageflow graphical viewer.
     */
    protected void configureEditPartViewer(EditPartViewer viewer_) {
        // configure the shared key handler
        if (null != viewer_.getKeyHandler()) {
            viewer_.getKeyHandler().setParent(getSharedKeyHandler());
        }
        // create the ActionRegistry
        createActions();

        // append the parent editor's action registry.
        ActionRegistry actionRegistry_ = (ActionRegistry) getParentEditor().getAdapter(ActionRegistry.class);
        if (actionRegistry_ != null) {
            for (Iterator iter = actionRegistry_.getActions(); iter.hasNext();) {
                getActionRegistry().registerAction((IAction) iter.next());
            }
        }
        // configure and register the context menu
        ContextMenuProvider provider = new PageflowEditorContextMenuProvider(viewer_, getActionRegistry());
        viewer_.setContextMenu(provider);
        getSite().registerContextMenu(EditorPlugin.getPluginId() + PAGEFLOW_CONTEXTMENU_REG_ID, provider,
                getSite().getSelectionProvider());

        // enable viewer as drop target for template transfers
        viewer_.addDropTargetListener(
                (TransferDropTargetListener) new PageflowTemplateTransferDropTargetListener(viewer_));

    }

    /**
     * Returns the pageflow that is edited.
     * 
     * @return - the pageflow that is edited
     */
    public Pageflow getPageflow() {
        return getPageflowManager().getModel();
    }

    /*
     * (non-Javadoc)
     * 
     * @see AbstractEditorPage#getGraphicalViewerForZoomSupport()
     */
    public GraphicalViewer getGraphicalViewer() {
        return viewer;
    }

    /**
     * @param contents
     */
    public void setGraphicalViewerContents(Object contents) {
        viewer.setContents(contents);
        propertyChange(null);
    }

    /*
     * (non-Javadoc)
     * 
     * @see IAdaptable#getAdapter(Class)
     */
    public Object getAdapter(Class type) {
        if (type == IContentOutlinePage.class) {
            return getOutlinePage();
        } else if (type == CommandStack.class) {
            return getCommandStack();
        } else if (type == ActionRegistry.class) {
            return getActionRegistry();
        } else if (type == IPropertySheetPage.class) {
            return getPropertySheetPage();
        } else if (type == ZoomManager.class) {
            return getZoomManager(getGraphicalViewer());
        }
        return super.getAdapter(type);
    }

    /**
     * Returns the outline page for the outline view with lazy creation
     * 
     * @return - the outline page
     */
    protected PageflowEditorOutlinePage getOutlinePage() {
        PageflowEditorOutlinePage outlinePage = new PageflowEditorOutlinePage(this);
        outlinePage.initialize(this);
        return outlinePage;
    }

    /**
     * Returns the undoable <code>PropertySheetPage</code> for this editor.
     * 
     * @return - the undoable <code>PropertySheetPage</code>
     */
    protected IPropertySheetPage getPropertySheetPage() {
        if (null == undoablePropertySheetPage) {
            undoablePropertySheetPage = new PropertySheetPage();

            /** set the property source for property sheet page */
            undoablePropertySheetPage.setRootEntry(new org.eclipse.gef.ui.properties.UndoablePropertySheetEntry(
                    (CommandStack) getAdapter(CommandStack.class)));

        }

        return undoablePropertySheetPage;
    }

    /**
     * Returns the selection syncronizer object. The synchronizer can be used to
     * sync the selection of 2 or more EditPartViewers.
     * 
     * @return - the syncrhonizer
     */
    protected SelectionSynchronizer getSelectionSynchronizer() {
        if (null == synchronizer) {
            synchronizer = new SelectionSynchronizer();
        }
        return synchronizer;
    }

    /**
     * Returns the shared KeyHandler that should be used for all viewers.
     * 
     * @return - the shared KeyHandler
     */
    protected KeyHandler getSharedKeyHandler() {
        if (null == sharedKeyHandler) {
            sharedKeyHandler = new KeyHandler();

            // configure common keys for all viewers
            sharedKeyHandler.put(KeyStroke.getPressed(SWT.DEL, 127, 0),
                    getActionRegistry().getAction(ActionFactory.DELETE.getId()));
            sharedKeyHandler.put(KeyStroke.getPressed(SWT.F2, 0),
                    getActionRegistry().getAction(GEFActionConstants.DIRECT_EDIT));
        }
        return sharedKeyHandler;
    }

    /**
     * Returns the selection listener.
     * 
     * @return - the <code>ISelectionListener</code>
     */
    protected ISelectionListener getSelectionListener() {
        return selectionListener;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
     */
    public void propertyChange(PropertyChangeEvent event) {
        String property = (event == null) ? null : event.getProperty();

        propagateProperty(property, viewer.getRootEditPart());
    }

    /**
     * propagate property change to children edit part
     * 
     * @param property -
     *            property's string name
     * @param part -
     *            parent edit part.
     */
    private void propagateProperty(String property, EditPart part) {
        processPropertyChange(property, part);

        if (part instanceof GraphicalEditPart) {
            // get the connections edit part
            Iterator iterConns = ((GraphicalEditPart) part).getSourceConnections().iterator();
            while (iterConns.hasNext()) {
                EditPart child = (EditPart) iterConns.next();
                propagateProperty(property, child);
            }
        }
        Iterator iter = part.getChildren().iterator();
        while (iter.hasNext()) {
            EditPart child = (EditPart) iter.next();
            propagateProperty(property, child);
        }
    }

    /**
     * process the property change FIXME: The property change should be category
     * to improve the performance.
     * 
     * @param property -
     *            property's string name
     * @param part
     */
    private void processPropertyChange(String property, EditPart part) {
        IPreferenceStore store = EditorPlugin.getDefault().getPreferenceStore();

        if (property != null && property.equals(GEMPreferences.USE_SYSTEM_COLORS)) {
            // reload all properties - it's easiest
            property = null;
        }

        if (property == null || GEMPreferences.SNAP_TO_GRID.equals(property)) {
            boolean bSnapToGrid = store.getBoolean(GEMPreferences.SNAP_TO_GRID);
            if (part instanceof ILayerPanePreference) {
                ((ILayerPanePreference) part).setGridVisible(bSnapToGrid);
            }
        }

        if (property == null || GEMPreferences.GRID_WIDTH.equals(property)
                || GEMPreferences.GRID_HEIGHT.equals(property)) {
            Dimension gridSpacing = new Dimension(store.getInt(GEMPreferences.GRID_WIDTH),
                    store.getInt(GEMPreferences.GRID_HEIGHT));
            if (part instanceof ILayerPanePreference) {
                ((ILayerPanePreference) part).setGridSpacing(gridSpacing);
            }
        }

        if (property == null || GEMPreferences.GRID_COLOR.equals(property)) {
            Color gridFgColor = GEMPreferences.getColor(store, GEMPreferences.GRID_COLOR);
            if (part instanceof ILayerPanePreference) {
                ((ILayerPanePreference) part).setGridForegroundColor(gridFgColor);
            }
        }

        if (property == null || GEMPreferences.CANVAS_COLOR.equals(property)) {
            Color containerBgColor = GEMPreferences.getColor(store, GEMPreferences.CANVAS_COLOR);
            if (part instanceof IFigurePreference) {
                ((IFigurePreference) part).setBackgroundColor(containerBgColor);
            }
        }

        if (property == null || GEMPreferences.LINE_WIDTH.equals(property)) {
            int linkLineWidth = store.getInt(GEMPreferences.LINE_WIDTH);

            if (part instanceof IConnectionPreference) {
                ((IConnectionPreference) part).setLineWidth(linkLineWidth);
            }
        }

        if (property == null || GEMPreferences.LINE_COLOR.equals(property)) {
            Color linkLineColor = GEMPreferences.getColor(store, GEMPreferences.LINE_COLOR);
            if (part instanceof IConnectionPreference) {
                ((IConnectionPreference) part).setForegroundColor(linkLineColor);
            }
        }

        if (property == null || GEMPreferences.SHOW_LINE_LABELS.equals(property)) {
            boolean bLinkLabelVisible = store.getBoolean(GEMPreferences.SHOW_LINE_LABELS);
            if (part instanceof IConnectionPreference) {
                ((IConnectionPreference) part).setLabelVisible(bLinkLabelVisible);
            }
        }

        if (property == null || GEMPreferences.LINE_LABEL_FONT.equals(property)
                || GEMPreferences.LINE_LABEL_FONT_COLOR.equals(property)) {
            Font linkLabelFont = getLinkLabelFont();
            Color linkLabelFgColor = GEMPreferences.getColor(store, GEMPreferences.LINE_LABEL_FONT_COLOR);
            if (part instanceof IConnectionPreference) {
                ((IConnectionPreference) part).setFont(linkLabelFont);
                ((IConnectionPreference) part).setLabelForegroundColor(linkLabelFgColor);
            }
        }

        if (property == null || GEMPreferences.LINE_LABEL_COLOR.equals(property)) {
            Color linkLabelBgColor = GEMPreferences.getColor(store, GEMPreferences.LINE_LABEL_COLOR);
            if (part instanceof IConnectionPreference) {
                ((IConnectionPreference) part).setLabelBackgroundColor(linkLabelBgColor);
            }
        }

        if (property == null || GEMPreferences.LINE_ROUTING.equals(property)) {
            String connectionStyle = store.getString(GEMPreferences.LINE_ROUTING);
            int style;
            if (GEMPreferences.LINE_ROUTING_MANHATTAN.equals(connectionStyle)) {
                style = ILayerPanePreference.LINE_ROUTING_MANHATTAN;
            } else {
                style = ILayerPanePreference.LINE_ROUTING_MANUAL;
            }

            if (part instanceof ILayerPanePreference) {
                ((ILayerPanePreference) part).setConnectionRouterStyle(style);
            } else if (part instanceof IConnectionPreference) {
                ((IConnectionPreference) part).setConnectionRouterStyle(style);
            }
        }

        if (property == null || GEMPreferences.FIGURE_LABEL_FONT.equals(property)
                || GEMPreferences.FIGURE_LABEL_FONT_COLOR.equals(property)) {
            Font nodeLabelFont = getNodeLabelFont();
            Color nodeLabelFgColor = GEMPreferences.getColor(store, GEMPreferences.FIGURE_LABEL_FONT_COLOR);

            if (part instanceof INodePreference) {
                ((INodePreference) part).setFont(nodeLabelFont);
                ((INodePreference) part).setForegroundColor(nodeLabelFgColor);
            }
        }

        if (property == null || GEMPreferences.LABEL_PLACEMENT.equals(property)) {
            int placement = PositionConstants.SOUTH;
            String nodeLabelPlacement = store.getString(GEMPreferences.LABEL_PLACEMENT);
            if (GEMPreferences.LABEL_PLACEMENT_TOP.equals(nodeLabelPlacement))
                placement = PositionConstants.NORTH;
            else if (GEMPreferences.LABEL_PLACEMENT_BOTTOM.equals(nodeLabelPlacement))
                placement = PositionConstants.SOUTH;
            else if (GEMPreferences.LABEL_PLACEMENT_LEFT.equals(nodeLabelPlacement))
                placement = PositionConstants.WEST;
            else if (GEMPreferences.LABEL_PLACEMENT_RIGHT.equals(nodeLabelPlacement))
                placement = PositionConstants.EAST;
            if (part instanceof INodePreference)
                ((INodePreference) part).setTextPlacement(placement);
        }
    }

    private Font getLinkLabelFont() {
        FontRegistry registry = JFaceResources.getFontRegistry();
        IPreferenceStore store = EditorPlugin.getDefault().getPreferenceStore();
        FontData fontData = PreferenceConverter.getFontData(store, GEMPreferences.LINE_LABEL_FONT);
        if (!registry.get(fontData.toString()).equals(registry.defaultFont()))
            return registry.get(fontData.toString());

        registry.put(fontData.toString(), new FontData[] { fontData });
        return registry.get(fontData.toString());
    }

    private Font getNodeLabelFont() {
        FontRegistry registry = JFaceResources.getFontRegistry();
        IPreferenceStore store = EditorPlugin.getDefault().getPreferenceStore();
        FontData fontData = PreferenceConverter.getFontData(store, GEMPreferences.FIGURE_LABEL_FONT);
        if (!registry.get(fontData.toString()).equals(registry.defaultFont()))
            return registry.get(fontData.toString());

        registry.put(fontData.toString(), new FontData[] { fontData });
        return registry.get(fontData.toString());
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ui.ide.IGotoMarker#gotoMarker(org.eclipse.core.resources.IMarker)
     */
    public void gotoMarker(IMarker marker) {
        // The LOCATION attribute in the marker should be the ID string
        Object id = null;
        try {
            id = marker.getAttribute(IMarker.LOCATION);
        } catch (CoreException e) {
            // Pageflow.PageflowEditor.Error.invalidMarkerAttribute = Unable to
            // get marker's attribute
            log.error("Pageflow.PageflowEditor.Error.invalidMarkerAttribute", //$NON-NLS-1$
                    e);
        }
        if (id instanceof String) {
            GraphicalEditPart part = EditPartMarkerUtil
                    .findEditPart((GraphicalEditPart) getGraphicalViewer().getRootEditPart(), (String) id);
            if (part != null) {
                getGraphicalViewer().reveal(part);
                getGraphicalViewer().select(part);
                return;
            }
        }

    }

    /**
     * the related web page is changed in outside editor, the pageflow should be
     * revalidated to update the validation icons
     * 
     * @param fullPath
     */
    public void webPageChanged(IPath fullPath) {
        PageflowPage page = getPageflowManager().foundPage(WebrootUtil.getWebPath(fullPath));

        if (page != null && getGraphicalViewer() != null && getGraphicalViewer().getRootEditPart() != null) {
            GraphicalEditPart pagePart = EditPartMarkerUtil
                    .findEditPart((GraphicalEditPart) getGraphicalViewer().getRootEditPart(), page.getId());
            PageflowAnnotationUtil.validatePage((PageflowNodeEditPart) pagePart);
        }
    }

    /**
     * Get the image desriptor from the view's id.
     * 
     * @param viewid
     * @return
     */
    private ImageDescriptor getImageDescriptorForView(String viewid) {
        IConfigurationElement[] elements = Platform.getExtensionRegistry()
                .getConfigurationElementsFor("org.eclipse.ui.views"); //$NON-NLS-1$
        for (int i = 0; i < elements.length; i++) {
            String name = elements[i].getName();
            String id = elements[i].getAttribute("id"); //$NON-NLS-1$
            if ("view".equals(name) && viewid.equals(id)) { //$NON-NLS-1$
                String iconPath = elements[i].getAttribute("icon"); //$NON-NLS-1$
                if (iconPath != null) {
                    return AbstractUIPlugin.imageDescriptorFromPlugin(
                            elements[i].getDeclaringExtension().getContributor().getName(), iconPath);
                }
            }
        }
        return null;
    }
}