| /******************************************************************************* |
| * Copyright (c) 2004, 2006 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; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.EventObject; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.resources.IProject; |
| 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.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.emf.common.command.BasicCommandStack; |
| import org.eclipse.emf.common.notify.AdapterFactory; |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.resource.ResourceSet; |
| import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain; |
| import org.eclipse.emf.edit.domain.EditingDomain; |
| import org.eclipse.emf.edit.domain.IEditingDomainProvider; |
| import org.eclipse.emf.edit.provider.ComposedAdapterFactory; |
| import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory; |
| import org.eclipse.emf.edit.provider.resource.ResourceItemProviderAdapterFactory; |
| import org.eclipse.gef.commands.CommandStack; |
| import org.eclipse.gef.commands.CommandStackListener; |
| import org.eclipse.gef.editparts.ZoomManager; |
| import org.eclipse.gef.ui.actions.ActionRegistry; |
| import org.eclipse.gef.ui.actions.EditorPartAction; |
| import org.eclipse.gef.ui.actions.SaveAction; |
| import org.eclipse.gef.ui.actions.UpdateAction; |
| import org.eclipse.jface.action.IAction; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.ISelectionChangedListener; |
| import org.eclipse.jface.viewers.ISelectionProvider; |
| import org.eclipse.jface.viewers.SelectionChangedEvent; |
| import org.eclipse.jface.viewers.StructuredSelection; |
| import org.eclipse.jst.jsf.common.ui.internal.actions.IOpenPage; |
| import org.eclipse.jst.jsf.core.IJSFCoreConstants; |
| import org.eclipse.jst.jsf.facesconfig.edit.provider.FacesConfigItemProviderAdapterFactory; |
| import org.eclipse.jst.jsf.facesconfig.emf.FacesConfigType; |
| import org.eclipse.jst.jsf.facesconfig.ui.page.ComponentsPage; |
| import org.eclipse.jst.jsf.facesconfig.ui.page.IntroductionPage; |
| import org.eclipse.jst.jsf.facesconfig.ui.page.ManagedBeanPage; |
| import org.eclipse.jst.jsf.facesconfig.ui.page.OthersPage; |
| import org.eclipse.jst.jsf.facesconfig.ui.page.OverviewPage; |
| import org.eclipse.jst.jsf.facesconfig.ui.page.WaitForLoadPage; |
| import org.eclipse.jst.jsf.facesconfig.ui.pageflow.DelegatingZoomManager; |
| import org.eclipse.jst.jsf.facesconfig.ui.pageflow.PageflowEditor; |
| import org.eclipse.jst.jsf.facesconfig.ui.pageflow.command.DelegatingCommandStack; |
| import org.eclipse.jst.jsf.facesconfig.ui.pageflow.command.EMFCommandStackGEFAdapter; |
| import org.eclipse.jst.jsf.facesconfig.ui.pageflow.layout.PageflowLayoutManager; |
| import org.eclipse.jst.jsf.facesconfig.ui.preference.GEMPreferences; |
| import org.eclipse.jst.jsf.facesconfig.util.FacesConfigArtifactEdit; |
| import org.eclipse.swt.custom.CTabFolder; |
| import org.eclipse.ui.IActionBars; |
| import org.eclipse.ui.IEditorActionBarContributor; |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.IEditorSite; |
| import org.eclipse.ui.IFileEditorInput; |
| import org.eclipse.ui.PartInitException; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.actions.WorkspaceModifyOperation; |
| import org.eclipse.ui.dialogs.SaveAsDialog; |
| import org.eclipse.ui.forms.editor.FormEditor; |
| import org.eclipse.ui.forms.editor.FormPage; |
| import org.eclipse.ui.forms.editor.IFormPage; |
| import org.eclipse.ui.ide.IDE; |
| import org.eclipse.ui.ide.IGotoMarker; |
| import org.eclipse.ui.part.FileEditorInput; |
| import org.eclipse.ui.views.contentoutline.IContentOutlinePage; |
| import org.eclipse.ui.views.properties.IPropertySheetPage; |
| import org.eclipse.ui.views.properties.tabbed.ITabbedPropertySheetPageContributor; |
| import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage; |
| import org.eclipse.wst.common.project.facet.core.IFacetedProject; |
| import org.eclipse.wst.common.project.facet.core.IProjectFacet; |
| import org.eclipse.wst.common.project.facet.core.IProjectFacetVersion; |
| import org.eclipse.wst.common.project.facet.core.ProjectFacetsManager; |
| import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; |
| import org.eclipse.wst.sse.ui.StructuredTextEditor; |
| |
| /** |
| * This is the main editor for the faces-config file. Note that the model |
| * load can involve long-running socket operations (shouldn't but can), |
| * so the editor UI is load asynchronously. This is means that any |
| * operations that need to be executed on editor open should be run |
| * using AddPagesTask.pageSafeExecute() to ensure that they occur |
| * after all editor pages have finished loading. |
| * |
| * @author sfshi |
| * |
| */ |
| public class FacesConfigEditor extends FormEditor implements |
| IEditingDomainProvider, ISelectionProvider { |
| |
| /** |
| * This editor's ID. TODO: this should prob be in plugin.properties? |
| */ |
| public static final String EDITOR_ID = "org.eclipse.jst.jsf.facesconfig.ui.FacesConfigEditor"; //$NON-NLS-1$ |
| |
| |
| /** |
| * Page id for Source page. Used for testing only. |
| */ |
| public static final String SOURCE_PAGE_ID = "SourcePageId"; //$NON-NLS-1$ |
| /** |
| * editing domain that is used to track all changes to the model |
| */ |
| private AdapterFactoryEditingDomain editingDomain; |
| |
| /** |
| * adapter factory used for providing views of the model |
| */ |
| private ComposedAdapterFactory adapterFactory; |
| |
| /** id of the pageflowPage */ |
| private int pageflowPageID; |
| |
| private int managedBeanPageID; |
| |
| private int componentsPageID; |
| |
| private int othersPageID; |
| |
| private int sourcePageId; |
| |
| private PageflowEditor pageflowPage; |
| |
| /** The source text editor. */ |
| private StructuredTextEditor sourcePage; |
| |
| private Collection selectionChangedListeners = new ArrayList(); |
| |
| private ISelection editorSelection = StructuredSelection.EMPTY; |
| |
| private IContentOutlinePage outlinePage; |
| |
| private IProject currentProject; |
| |
| private boolean isWebProject; |
| |
| private ModelLoader _modelLoader; |
| |
| /** |
| * only true once dispose() has been called |
| * used to signal that the editor was disposed. |
| */ |
| private boolean _isDisposed; // = false; |
| |
| /** |
| * Used to load editor pages when the model is loaded |
| */ |
| private final AddPagesTask _addPagesTask = new AddPagesTask(); |
| |
| /** |
| * Default constructor |
| */ |
| public FacesConfigEditor() { |
| initializeEMF(); |
| } |
| |
| /** |
| * This listens for workspace changes. <!-- begin-user-doc --> <!-- |
| * end-user-doc --> |
| * |
| * @generated |
| */ |
| protected IResourceChangeListener resourceChangeListener = new IResourceChangeListener() { |
| public void resourceChanged(IResourceChangeEvent event) { |
| // Only listening to these. |
| // if (event.getType() == IResourceDelta.POST_CHANGE) |
| { |
| IResourceDelta delta = event.getDelta(); |
| try { |
| class ResourceDeltaVisitor implements IResourceDeltaVisitor { |
| protected ResourceSet resourceSet = editingDomain |
| .getResourceSet(); |
| |
| @SuppressWarnings("hiding") |
| protected Collection changedResources = new ArrayList(); |
| |
| @SuppressWarnings("hiding") |
| protected Collection removedResources = new ArrayList(); |
| |
| public boolean visit(IResourceDelta delta_) { |
| if (delta_.getFlags() != IResourceDelta.MARKERS |
| && delta_.getResource().getType() == IResource.FILE) { |
| if ((delta_.getKind() & (IResourceDelta.CHANGED | IResourceDelta.REMOVED)) != 0) { |
| Resource resource = resourceSet |
| .getResource(URI.createURI(delta_ |
| .getFullPath().toString()), |
| false); |
| if (resource != null) { |
| if ((delta_.getKind() & IResourceDelta.REMOVED) != 0) { |
| removedResources.add(resource); |
| } else { |
| changedResources.add(resource); |
| } |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| public Collection getChangedResources() { |
| return changedResources; |
| } |
| |
| public Collection getRemovedResources() { |
| return removedResources; |
| } |
| } |
| |
| ResourceDeltaVisitor visitor = new ResourceDeltaVisitor(); |
| delta.accept(visitor); |
| |
| if (!visitor.getRemovedResources().isEmpty()) { |
| removedResources.addAll(visitor.getRemovedResources()); |
| if (!isDirty()) { |
| getSite().getShell().getDisplay().asyncExec( |
| new Runnable() { |
| public void run() { |
| getSite().getPage().closeEditor( |
| FacesConfigEditor.this, |
| false); |
| FacesConfigEditor.this.dispose(); |
| } |
| }); |
| } |
| } |
| |
| if (!visitor.getChangedResources().isEmpty()) { |
| changedResources.addAll(visitor.getChangedResources()); |
| } |
| } catch (CoreException exception) { |
| // log it. |
| EditorPlugin.getDefault().getLog().log( |
| new Status(IStatus.ERROR, EditorPlugin |
| .getPluginId(), IStatus.OK, exception |
| .getMessage() == null ? "" : exception //$NON-NLS-1$ |
| .getMessage(), exception)); |
| } |
| } |
| } |
| }; |
| |
| /** |
| * Resources that have been removed since last activation. |
| * |
| * @generated |
| */ |
| Collection removedResources = new ArrayList(); |
| |
| /** |
| * Resources that have been changed since last activation. |
| * |
| * @generated |
| */ |
| Collection changedResources = new ArrayList(); |
| |
| /** |
| * Resources that have been saved. |
| * |
| * @generated |
| */ |
| Collection savedResources = new ArrayList(); |
| |
| /** |
| * Initializes the EMF support. |
| */ |
| private void initializeEMF() { |
| // create an adapter factory that yields item providers |
| List factories = new ArrayList(); |
| factories.add(new ResourceItemProviderAdapterFactory()); |
| factories.add(new FacesConfigItemProviderAdapterFactory()); |
| factories.add(new ReflectiveItemProviderAdapterFactory()); |
| adapterFactory = new ComposedAdapterFactory(factories); |
| |
| // create the command stack that will notify this editor as commands are |
| // executed |
| BasicCommandStack commandStack = new BasicCommandStack(); |
| commandStack |
| .addCommandStackListener(new org.eclipse.emf.common.command.CommandStackListener() { |
| public void commandStackChanged(final EventObject event) { |
| getContainer().getShell().getDisplay().asyncExec( |
| new Runnable() { |
| public void run() { |
| editorDirtyStateChanged(); |
| getActionBarContributor() |
| .updateActionBars(); |
| } |
| }); |
| } |
| }); |
| // commandStack.addCommandStackListener(this); |
| // create the editing domain with a special command stack |
| editingDomain = new AdapterFactoryEditingDomain(adapterFactory, |
| commandStack, new HashMap()); |
| } |
| |
| /* |
| * @see org.eclipse.ui.IEditorPart#init(org.eclipse.ui.IEditorSite, |
| * org.eclipse.ui.IEditorInput) |
| */ |
| public void init(IEditorSite site, IEditorInput input) |
| throws PartInitException { |
| try { |
| super.init(site, input); |
| } catch (Exception e) { |
| MessageDialog.openError(null, |
| EditorMessages.FacesConfigEditor_Error_OpenModel_Title, |
| EditorMessages.FacesConfigEditor_Error_OpenModel); |
| throw new PartInitException( |
| EditorMessages.FacesConfigEditor_Error_OpenModel); |
| } |
| |
| setPartName(input.getName()); |
| if (!isValidInput(input)) { |
| PlatformUI.getWorkbench().getActiveWorkbenchWindow() |
| .getActivePage().openEditor(input, |
| "org.eclipse.ui.DefaultTextEditor"); //$NON-NLS-1$ |
| |
| close(false); |
| return; |
| } |
| |
| createActions(); |
| |
| ResourcesPlugin.getWorkspace().addResourceChangeListener( |
| resourceChangeListener, IResourceChangeEvent.POST_CHANGE); |
| } |
| |
| /* |
| * @see org.eclipse.ui.part.EditorPart#setInput(org.eclipse.ui.IEditorInput) |
| */ |
| protected void setInput(IEditorInput input) |
| { |
| isWebProject = matches(input); |
| super.setInput(input); |
| |
| IFile inputFile = (IFile) input.getAdapter(IFile.class); |
| if (inputFile != null) |
| { |
| final IProject project = inputFile.getProject(); |
| final IPath inputPath = inputFile.getFullPath(); |
| |
| _modelLoader = new ModelLoader(); |
| _modelLoader.load(project, inputPath, isWebProject, _addPagesTask); |
| } |
| } |
| |
| |
| protected void addPages() |
| { |
| // try loading wait page |
| // if we get to here before model load completes, |
| // then wait page will give the user the indication |
| // that something is happening in the background before |
| // the editor full loads. |
| // if the model is already loaded, this call should do nothing |
| _addPagesTask.maybeAddWaitPage(); |
| } |
| |
| /** |
| * This runnable is used to used to manage the loading of the |
| * editor pages for editor in a deferred fashion. Because the model |
| * loading for this editor can be noticably long and (unfortunately) |
| * may involve socket calls that block, loadModel(), runs this on a |
| * separate thread. This class is intended to be used in two ways: |
| * |
| * 1) by the model loading code to signal it is finished by executing |
| * the run() via a display.asyncExec(). |
| * |
| * 2) by the addPages() call back on the the main editor as a way to |
| * load a "Please wait for loading" page if the loading is still running |
| * by the time the editor is ready to visualize itself. |
| * |
| * Note that in both cases methods of this class *must* be running on the |
| * main display thread. |
| * |
| * @author cbateman |
| * |
| */ |
| private class AddPagesTask extends ModelLoader.ModelLoaderComplete |
| { |
| private final AtomicBoolean _arePagesLoaded = new AtomicBoolean(false); // set to true when the regular editor pages are loaded |
| private FormPage _waitPage; |
| private List<Runnable> _deferredRunnables = new ArrayList<Runnable>(); |
| |
| /** |
| * If the editor pages are loaded, runnable.run() is invoked immediately |
| * If the editor pages are not loaded yet, runnable is queued and will be |
| * executed in the order they are added immediately after the pages are loaded |
| * |
| * @param runnable |
| */ |
| public synchronized void pageSafeExecute(Runnable runnable) |
| { |
| if (!_isDisposed) |
| { |
| if (!_arePagesLoaded.get()) |
| { |
| _deferredRunnables.add(runnable); |
| } |
| else |
| { |
| runnable.run(); |
| } |
| } |
| } |
| |
| /** |
| * @return true if the pages are loaded |
| */ |
| public synchronized boolean getArePagesLoaded() |
| { |
| return _arePagesLoaded.get(); |
| } |
| |
| /** |
| * Remove the wait page if present. |
| */ |
| public synchronized void removeWaitPage() |
| { |
| if (_waitPage != null |
| && !_waitPage.getPartControl().isDisposed()) |
| { |
| int index = _waitPage.getIndex(); |
| |
| if (index >= 0) |
| { |
| removePage(index); |
| } |
| } |
| } |
| |
| /** |
| * Add the wait page if the main pages aren't already loaded |
| */ |
| public synchronized void maybeAddWaitPage() |
| { |
| // only load the wait page if the other pages haven't been loaded |
| if (!getArePagesLoaded()) |
| { |
| _waitPage = new WaitForLoadPage(FacesConfigEditor.this, "WaitForLoad", EditorMessages.FacesConfigEditor_WaitForLoad_EditorTabTitle); //$NON-NLS-1$ |
| |
| try |
| { |
| addPage(0,_waitPage); |
| } |
| catch(PartInitException pie) |
| { |
| _waitPage =null; |
| EditorPlugin.getDefault().getLog().log( |
| new Status(IStatus.ERROR, EditorPlugin.getPluginId(), |
| IStatus.OK, pie.getMessage() == null ? "" : pie //$NON-NLS-1$ |
| .getMessage(), pie)); |
| } |
| } |
| } |
| |
| /** |
| * Must be run on the UI thread |
| */ |
| public void doRun(FacesConfigArtifactEdit edit) |
| { |
| synchronized(this) |
| { |
| // ensure wait page gets removed |
| removeWaitPage(); |
| |
| if (!getArePagesLoaded() |
| && !_isDisposed) // NOTE: we assume that access to variable does not need to |
| // to be synchronous since this method must |
| // be run on the UI thread. The only way |
| // that isDisposed should be true is if model loading took a long |
| // time and the user closed the editor before it completed (trigger dispose to be called) |
| { |
| try |
| { |
| if (isWebProject && edit != null && edit.getFacesConfig() != null) |
| { |
| // only add the intro editor if the preference |
| // is set to do so. |
| if (GEMPreferences.getShowIntroEditor()) |
| { |
| IntroductionPage page1 = new IntroductionPage(FacesConfigEditor.this); |
| addPage(page1, null); |
| } |
| |
| IFormPage overviewPage = new OverviewPage(FacesConfigEditor.this); |
| addPage(overviewPage, null); |
| |
| // Page flow |
| createAndAddPageflowPage(); |
| |
| // pages |
| IFormPage managedBeanPage = new ManagedBeanPage(FacesConfigEditor.this); |
| managedBeanPageID = addPage(managedBeanPage, null); |
| IFormPage componentsPage = new ComponentsPage(FacesConfigEditor.this); |
| componentsPageID = addPage(componentsPage, null); |
| IFormPage othersPage = new OthersPage(FacesConfigEditor.this); |
| othersPageID = addPage(othersPage, null); |
| } |
| |
| sourcePage = new StructuredTextEditor(); |
| |
| sourcePage.setEditorPart(FacesConfigEditor.this); |
| |
| sourcePageId = addPage(sourcePage, FacesConfigEditor.this.getEditorInput()); |
| setPageText(sourcePageId, |
| EditorMessages.FacesConfigEditor_Source_TabName); |
| sourcePage.update(); |
| |
| // default active page to 0 |
| setActivePage(0); |
| |
| // execute deferred runnables |
| for (Runnable runnable : _deferredRunnables) |
| { |
| runnable.run(); |
| } |
| |
| // flag the fact that the regular editor pages have been added |
| _arePagesLoaded.set(true); |
| } catch (PartInitException e) { |
| EditorPlugin.getDefault().getLog().log( |
| new Status(IStatus.ERROR, EditorPlugin.getPluginId(), |
| IStatus.OK, e.getMessage() == null ? "" : e //$NON-NLS-1$ |
| .getMessage(), e)); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Creates the pageflow page of the multi-page editor. |
| * @throws PartInitException |
| */ |
| protected void createAndAddPageflowPage() throws PartInitException { |
| pageflowPage = new PageflowEditor(this); |
| pageflowPageID = addPage(pageflowPage, getEditorInput()); |
| setPageText(pageflowPageID, |
| EditorMessages.FacesConfigEditor_Pageflow_TabName); |
| addPageActionRegistry(pageflowPage); |
| pageflowPage.getModelsTransform().setFacesConfig(getFacesConfig()); |
| pageflowPage.getModelsTransform().setPageflow( |
| pageflowPage.getPageflow()); |
| boolean fornew = pageflowPage.getModelsTransform() |
| .updatePageflowModelFromEMF(); |
| pageflowPage.setGraphicalViewerContents(pageflowPage.getPageflow()); |
| if (fornew) { |
| PageflowLayoutManager.getInstance().layoutPageflow( |
| pageflowPage.getPageflow()); |
| } |
| pageflowPage.getModelsTransform().setListenToNotify(true); |
| } |
| |
| /** |
| * TODO: this is used only for testing |
| * @return the page flow editor |
| */ |
| public PageflowEditor getPageflowPage() { |
| return pageflowPage; |
| } |
| |
| /** |
| * get the action's registry of sub pages. |
| * @param page |
| * |
| */ |
| protected void addPageActionRegistry(IEditorPart page) { |
| if (page != null) { |
| ActionRegistry pageActionRegisty = (ActionRegistry) page |
| .getAdapter(ActionRegistry.class); |
| if (pageActionRegisty != null) { |
| for (Iterator iter = pageActionRegisty.getActions(); iter |
| .hasNext();) { |
| getActionRegistry().registerAction((IAction) iter.next()); |
| } |
| } |
| } |
| } |
| |
| /** the editor's action registry */ |
| private ActionRegistry actionRegistry = null; |
| |
| /** |
| * Returns the action registry of this editor. |
| * |
| * @return - the action registry |
| */ |
| protected ActionRegistry getActionRegistry() { |
| if (null == actionRegistry) |
| actionRegistry = new ActionRegistry(); |
| |
| return actionRegistry; |
| } |
| |
| /** |
| * Returns the root object of the configuration model. |
| * |
| * @return the root object. Should not, but may return null. |
| */ |
| public FacesConfigType getFacesConfig() |
| { |
| FacesConfigArtifactEdit edit = _modelLoader.getEdit(); |
| if (edit != null) |
| { |
| return edit.getFacesConfig(); |
| } |
| return null; |
| } |
| |
| /* |
| * @see org.eclipse.ui.ISaveablePart#isDirty() |
| */ |
| public boolean isDirty() { |
| return ((BasicCommandStack) editingDomain.getCommandStack()) |
| .isSaveNeeded() |
| || super.isDirty(); |
| } |
| |
| /** |
| * This class listens for command stack changes of the pages contained in |
| * this editor and decides if the editor is dirty or not. |
| */ |
| private class MultiPageCommandStackListener implements CommandStackListener { |
| |
| /** the observed command stacks */ |
| private List commandStacks = new ArrayList(2); |
| |
| /** to get the editorpart from command stack */ |
| private HashMap mapEditorCommandStack = new HashMap(); |
| |
| private boolean saveLocation = false; |
| |
| /** |
| * Adds a <code>CommandStack</code> to observe. |
| * |
| * @param commandStack |
| * @param editor |
| */ |
| public void addCommandStack(CommandStack commandStack, |
| IEditorPart editor) { |
| if (commandStack == null) |
| return; |
| |
| if (mapEditorCommandStack.get(commandStack) == editor) |
| return; |
| |
| commandStacks.add(commandStack); |
| commandStack.addCommandStackListener(this); |
| mapEditorCommandStack.put(commandStack, editor); |
| } |
| |
| /** |
| * set the dirty status for the models of different editor |
| * |
| * @param editor - |
| * editor, e.g., pageflow or databinding page. |
| * @param dirty - |
| * true or false |
| */ |
| private void setEditorDirty(IEditorPart editor, boolean dirty) { |
| // do nothing |
| } |
| |
| /** the list of action ids that are to CommandStack actions */ |
| private List stackActionIDs = new ArrayList(); |
| |
| /** |
| * Updates the specified actions. |
| * |
| * @param actionIds - |
| * the list of ids of actions to update |
| */ |
| private 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(); |
| } |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see CommandStackListener#commandStackChanged(java.util.EventObject) |
| */ |
| public void commandStackChanged(EventObject event) { |
| // enable or disable the actions |
| updateActions(stackActionIDs); |
| if (((CommandStack) event.getSource()).isDirty()) { |
| // set the editor's model dirty status |
| setEditorDirty((IEditorPart) mapEditorCommandStack |
| .get(event.getSource()), true); |
| // at least one command stack is dirty, |
| // so the multi page editor is dirty too |
| setDirty(true); |
| } else { |
| // set the editor's model dirty status, if it is from not save |
| // location. |
| if (!saveLocation) { |
| setEditorDirty((IEditorPart) mapEditorCommandStack |
| .get(event.getSource()), true); |
| setDirty(true); |
| } else { |
| setDirty(false); |
| } |
| } |
| } |
| |
| /** the pageflow page editor's dirty state */ |
| private boolean isDirty = false; |
| |
| /** |
| * Changes the dirty state. |
| * |
| * @param dirty - |
| * dirty state |
| */ |
| public void setDirty(boolean dirty) { |
| if (isDirty != dirty) { |
| isDirty = dirty; |
| firePropertyChange(IEditorPart.PROP_DIRTY); |
| } |
| } |
| |
| /** |
| * Disposed the listener |
| */ |
| public void dispose() { |
| for (Iterator stacks = commandStacks.iterator(); stacks.hasNext();) { |
| ((CommandStack) stacks.next()).removeCommandStackListener(this); |
| } |
| commandStacks.clear(); |
| } |
| |
| /** |
| * Marks every observed command stack beeing saved. This method should |
| * be called whenever the editor/model was saved. |
| */ |
| public void markSaveLocations() { |
| saveLocation = true; |
| for (Iterator stacks = commandStacks.iterator(); stacks.hasNext();) { |
| CommandStack stack = (CommandStack) stacks.next(); |
| stack.markSaveLocation(); |
| } |
| saveLocation = false; |
| } |
| |
| /** |
| * Flushes every observed command stack and resets the save location to |
| * zero. |
| */ |
| public void flush() { |
| for (Iterator stacks = commandStacks.iterator(); stacks.hasNext();) { |
| CommandStack stack = (CommandStack) stacks.next(); |
| stack.flush(); |
| } |
| } |
| } |
| |
| /** the <code>CommandStackListener</code> */ |
| private MultiPageCommandStackListener multiPageCommandStackListener = null; |
| |
| /** |
| * Returns the global command stack listener. |
| * |
| * @return the <code>CommandStackListener</code> |
| */ |
| protected MultiPageCommandStackListener getMultiPageCommandStackListener() { |
| if (null == multiPageCommandStackListener) |
| multiPageCommandStackListener = new MultiPageCommandStackListener(); |
| |
| return multiPageCommandStackListener; |
| } |
| |
| /* |
| * @see org.eclipse.ui.ISaveablePart#doSave(org.eclipse.core.runtime.IProgressMonitor) |
| */ |
| public void doSave(IProgressMonitor monitor) { |
| // do the work within an operation because this is a long running |
| // activity that modifies the workbench |
| WorkspaceModifyOperation operation = new WorkspaceModifyOperation() { |
| public void execute(IProgressMonitor monitor_) { |
| try { |
| if (isWebProject && |
| _modelLoader.getEdit() != null) { |
| // modelResource.save(Collections.EMPTY_MAP); |
| _modelLoader.getEdit() |
| .getDeploymentDescriptorResource().save( |
| Collections.EMPTY_MAP); |
| IFile file = ((IFileEditorInput) getEditorInput()) |
| .getFile(); |
| pageflowPage.doSave(file, monitor_); |
| } |
| sourcePage.doSave(monitor_); |
| getMultiPageCommandStackListener().markSaveLocations(); |
| } catch (Exception e) { |
| EditorPlugin.getDefault().getLog().log( |
| new Status(IStatus.ERROR, EditorPlugin |
| .getPluginId(), IStatus.OK, |
| e.getMessage() == null ? "" : e //$NON-NLS-1$ |
| .getMessage(), e)); |
| } |
| } |
| }; |
| try { |
| // commit all pending changes in form pages |
| for (Iterator iter = pages.iterator(); iter.hasNext();) { |
| Object obj = iter.next(); |
| if (obj instanceof FormPage) { |
| ((FormPage) obj).doSave(monitor); |
| } |
| // else if (obj instanceof PageflowEditor) { |
| // ((PageflowEditor) obj).doSave(monitor); |
| // } |
| |
| } |
| operation.run(null);// .run(true, false, |
| // operation; |
| // runs the operation, and shows progress |
| // new ProgressMonitorDialog(); |
| |
| // refresh the necessary state |
| ((BasicCommandStack) editingDomain.getCommandStack()).saveIsDone(); |
| |
| editorDirtyStateChanged(); |
| } catch (Exception e) { |
| EditorPlugin.getDefault().getLog().log( |
| new Status(IStatus.ERROR, EditorPlugin.getPluginId(), |
| IStatus.OK, e.getMessage(), e)); |
| } |
| } |
| |
| public void doSaveAs() { |
| SaveAsDialog saveAsDialog = new SaveAsDialog(getSite().getShell()); |
| saveAsDialog.open(); |
| IPath path = saveAsDialog.getResult(); |
| if (path != null) { |
| IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(path); |
| if (file != null) { |
| doSaveAs(URI.createPlatformResourceURI(file.getFullPath() |
| .toString(), false), new FileEditorInput(file)); |
| } |
| } |
| } |
| |
| /** |
| * @param uri |
| * @param editorInput |
| */ |
| protected void doSaveAs(URI uri, IEditorInput editorInput) { |
| editingDomain.getResourceSet().getResources().get(0) |
| .setURI(uri); |
| setInputWithNotify(editorInput); |
| setPartName(editorInput.getName()); |
| IProgressMonitor progressMonitor = getActionBars() |
| .getStatusLineManager() != null ? getActionBars() |
| .getStatusLineManager().getProgressMonitor() |
| : new NullProgressMonitor(); |
| doSave(progressMonitor); |
| } |
| |
| public boolean isSaveAsAllowed() { |
| return true; |
| } |
| |
| /** |
| * Returns the <code>TabbedPropertySheetPage</code> for this editor. |
| * |
| * @return - the <code>TabbedPropertySheetPage</code> |
| */ |
| protected IPropertySheetPage getPropertySheetPage() { |
| return new TabbedPropertySheetPage( |
| new ITabbedPropertySheetPageContributor() { |
| |
| public String getContributorId() { |
| return EDITOR_ID; |
| } |
| }); |
| } |
| |
| /** the delegating ZoomManager */ |
| private DelegatingZoomManager delegatingZoomManager = null; |
| |
| /** |
| * check whether the input is related with IFile. |
| * |
| * @param input |
| * @return |
| */ |
| private boolean isValidInput(IEditorInput input) { |
| if (input != null) { |
| IFile file = (IFile) input.getAdapter(IResource.class); |
| if (file != null) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Returns the <code>DelegatingZoomManager</code> for this editor. |
| * |
| * @return - the <code>DelegatingZoomManager</code> |
| */ |
| protected DelegatingZoomManager getDelegatingZoomManager() { |
| if (!isValidInput(getEditorInput()) || !isWebProject || !_addPagesTask.getArePagesLoaded()) { |
| return null; |
| } |
| if (null == delegatingZoomManager) { |
| delegatingZoomManager = new DelegatingZoomManager(); |
| delegatingZoomManager |
| .setCurrentZoomManager((ZoomManager) pageflowPage |
| .getAdapter(ZoomManager.class)); |
| } |
| return delegatingZoomManager; |
| } |
| |
| /** the delegating CommandStack */ |
| private DelegatingCommandStack delegatingCommandStack = null; |
| |
| /** |
| * Returns the <code>CommandStack</code> for this editor. |
| * |
| * @return - the <code>CommandStack</code> |
| */ |
| public DelegatingCommandStack getDelegatingCommandStack() { |
| if (null == delegatingCommandStack) { |
| delegatingCommandStack = new DelegatingCommandStack(); |
| } |
| return delegatingCommandStack; |
| } |
| |
| /* |
| * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class) |
| */ |
| public Object getAdapter(Class adapter) { |
| if (adapter == IEditingDomainProvider.class) { |
| return new IEditingDomainProvider() { |
| public EditingDomain getEditingDomain() { |
| return editingDomain; |
| } |
| }; |
| } |
| if (adapter == EditingDomain.class) { |
| return editingDomain; |
| } |
| if (adapter == AdapterFactory.class) { |
| return adapterFactory; |
| } |
| if (adapter == IEditorPart.class) { |
| return getActiveEditor(); |
| } |
| |
| if (adapter == CommandStack.class) { |
| return getDelegatingCommandStack(); |
| } |
| if (adapter == ZoomManager.class) { |
| return getDelegatingZoomManager(); |
| } |
| |
| if (adapter == ActionRegistry.class) { |
| return getActionRegistry(); |
| } |
| if (adapter == IGotoMarker.class) { |
| return new IGotoMarker() { |
| public void gotoMarker(final IMarker marker) { |
| // this may be called on an editor open (i.e. double-click the Problems view) |
| // so ensure it runs safely with respect to the page load |
| _addPagesTask.pageSafeExecute(new Runnable() |
| { |
| public void run() |
| { |
| FacesConfigEditor.this.gotoMarker(marker); |
| } |
| }); |
| } |
| }; |
| } |
| if (adapter == StructuredTextEditor.class) { |
| return sourcePage; |
| } |
| |
| if (adapter == IContentOutlinePage.class) { |
| return getOutlinePage(); |
| } |
| |
| if (adapter == IPropertySheetPage.class) { |
| return getPropertySheetPage(); |
| } |
| |
| if (adapter == IProject.class) { |
| return getProject(); |
| } |
| |
| if (adapter == CTabFolder.class) { |
| return getContainer(); |
| } |
| |
| if (adapter == IOpenPage.class) { |
| return new IOpenPage() { |
| |
| public void setActiveEditorPage(String pageID) { |
| FacesConfigEditor.this.setActiveEditorPage(pageID); |
| |
| } |
| }; |
| } |
| |
| return super.getAdapter(adapter); |
| } |
| |
| private EMFCommandStackGEFAdapter sourceCommandStack; |
| |
| /** |
| * get or create the source page's GEF command stack based on its EMF |
| * command stack. |
| * |
| * @return |
| */ |
| private CommandStack getSourcePageCommandStack() { |
| if (sourceCommandStack == null) { |
| IDocument doc = sourcePage.getDocumentProvider().getDocument(getEditorInput()); |
| if (doc instanceof IStructuredDocument) { |
| sourceCommandStack = new EMFCommandStackGEFAdapter(doc); |
| } |
| else |
| { |
| EditorPlugin.getDefault().getLog().log( |
| new Status(IStatus.ERROR, EditorPlugin.getPluginId(), 0, |
| "Error getting undo stack for Faces Config editor. Undo may be disabled", //$NON-NLS-1$ |
| new Throwable())); |
| } |
| } |
| return sourceCommandStack; |
| } |
| |
| /** the list of action ids that are to CommandStack actions */ |
| // private List stackActionIDs = new ArrayList(); |
| /** the list of action ids that are editor actions */ |
| private List editorActionIDs = new ArrayList(); |
| |
| /** |
| * Adds an editor action to this editor. |
| * <p> |
| * Editor actions are actions that depend and work on the editor. |
| * |
| * @param action - |
| * the editor action |
| */ |
| protected void addEditorAction(EditorPartAction action) { |
| getActionRegistry().registerAction(action); |
| editorActionIDs.add(action.getId()); |
| } |
| |
| /** |
| * Creates different kinds of actions and registers them to the |
| * ActionRegistry. |
| */ |
| protected void createActions() { |
| // register save action |
| addEditorAction(new SaveAction(this)); |
| } |
| |
| /** |
| * Indicates that the current page has changed. |
| * <p> |
| * We update the DelegatingCommandStack, OutlineViewer and other things |
| * here. // |
| */ |
| protected void currentPageChanged() { |
| IEditorPart activeEditor = getActiveEditor(); |
| if (activeEditor == null) { |
| return; |
| } |
| |
| // update command stack |
| CommandStack cmdStack = null; |
| |
| if (activeEditor == pageflowPage) { |
| cmdStack = (CommandStack) activeEditor |
| .getAdapter(CommandStack.class); |
| } else if (activeEditor == sourcePage)// other page will delegate the |
| // GEF command stack to source |
| // page's. |
| { |
| cmdStack = this.getSourcePageCommandStack(); |
| } |
| |
| // Add command stacks |
| getMultiPageCommandStackListener().addCommandStack(cmdStack, |
| activeEditor); |
| getDelegatingCommandStack().setCurrentCommandStack(cmdStack); |
| |
| // enable or disable the actions |
| // updateActions(stackActionIDs); |
| |
| // update zoom actions |
| ZoomManager zoomManager = null; |
| zoomManager = (ZoomManager) activeEditor.getAdapter(ZoomManager.class); |
| |
| if (zoomManager != null) { |
| getDelegatingZoomManager().setCurrentZoomManager(zoomManager); |
| } |
| |
| IEditorActionBarContributor contributor = getEditorSite() |
| .getActionBarContributor(); |
| if (contributor != null |
| && contributor instanceof FacesConfigActionBarContributor) { |
| ((FacesConfigActionBarContributor) contributor) |
| .setActivePage(activeEditor); |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see MultiPageEditorPart#pageChange(int) |
| */ |
| protected void pageChange(int newPageIndex) { |
| super.pageChange(newPageIndex); |
| // getActionBarContributor().setActivePage(getActiveEditor()); |
| // refresh content depending on current page |
| currentPageChanged(); |
| } |
| |
| public void dispose() |
| { |
| // signal that we have been disposed |
| // do this before anything else |
| _isDisposed = true; |
| _modelLoader.dispose(); |
| |
| ResourcesPlugin.getWorkspace().removeResourceChangeListener( |
| resourceChangeListener); |
| |
| adapterFactory.dispose(); |
| |
| if (this.outlinePage != null) |
| outlinePage.dispose(); |
| |
| if (sourcePage != null) |
| sourcePage.dispose(); |
| |
| if (sourceCommandStack != null) |
| sourceCommandStack.dispose(); |
| |
| if (pageflowPage != null) |
| pageflowPage.dispose(); |
| |
| if (multiPageCommandStackListener != null) |
| multiPageCommandStackListener.dispose(); |
| |
| //do not call dispose on delegatingCommandStack. source and multiPage are already disposed |
| |
| super.dispose(); |
| } |
| |
| /** |
| * get the project of the faces config file that the editor is working on. |
| * |
| * @return IProject |
| */ |
| public IProject getProject() { |
| if (currentProject == null) { |
| if (_modelLoader.getEdit() != null) { |
| IFile file = _modelLoader.getEdit().getFile(); |
| if (file != null) |
| currentProject = file.getProject(); |
| } |
| } |
| return currentProject; |
| } |
| |
| public EditingDomain getEditingDomain() { |
| return editingDomain; |
| } |
| |
| /** |
| * Returns the <code>IContentOutlinePage</code> for this editor. |
| * |
| * @return - the <code>IContentOutlinePage</code> |
| */ |
| protected IContentOutlinePage getOutlinePage() { |
| if (null == outlinePage) { |
| outlinePage = new MultiPageEditorOutlinePage(); |
| } |
| return outlinePage; |
| } |
| |
| public void addSelectionChangedListener(ISelectionChangedListener listener) { |
| selectionChangedListeners.add(listener); |
| } |
| |
| public ISelection getSelection() { |
| return editorSelection; |
| } |
| |
| public void removeSelectionChangedListener( |
| ISelectionChangedListener listener) { |
| selectionChangedListeners.remove(listener); |
| } |
| |
| public void setSelection(ISelection selection) { |
| editorSelection = selection; |
| for (Iterator listeners = selectionChangedListeners.iterator(); listeners |
| .hasNext();) { |
| ISelectionChangedListener listener = (ISelectionChangedListener) listeners |
| .next(); |
| listener |
| .selectionChanged(new SelectionChangedEvent(this, selection)); |
| } |
| } |
| |
| private void gotoMarker(IMarker marker) { |
| setActivePage(sourcePageId); |
| IDE.gotoMarker(this.sourcePage, marker); |
| } |
| |
| /** |
| * FIXME: this is used only for testing. Should isolate better |
| * @return the action bar |
| */ |
| public FacesConfigActionBarContributor getActionBarContributor() { |
| return (FacesConfigActionBarContributor) getEditorSite() |
| .getActionBarContributor(); |
| } |
| |
| private IActionBars getActionBars() { |
| return getActionBarContributor().getActionBars(); |
| } |
| |
| public Object getSelectedPage() { |
| IFormPage page = getActivePageInstance(); |
| if (page != null) |
| return page; |
| |
| if (getActiveEditor() instanceof PageflowEditor) |
| return getActiveEditor(); |
| |
| return null; |
| } |
| |
| /** |
| * Shows a dialog that asks if conflicting changes should be discarded. |
| * @return the user's response. |
| */ |
| protected boolean handleDirtyConflict() { |
| return MessageDialog |
| .openQuestion( |
| getSite().getShell(), |
| EditorMessages.FacesConfigEditor_ErrorHandlingUndoConflicts_DialogTitle, |
| EditorMessages.FacesConfigEditor_ErrorHandlingUndoConflicts_DialogMessage); |
| } |
| |
| /** |
| * Handles what to do with changed resources on activation. |
| * |
| * @generated |
| */ |
| protected void handleChangedResources() { |
| if (!changedResources.isEmpty() |
| && (!isDirty() || handleDirtyConflict())) { |
| editingDomain.getCommandStack().flush(); |
| |
| for (Iterator i = changedResources.iterator(); i.hasNext();) { |
| Resource resource = (Resource) i.next(); |
| if (resource.isLoaded()) { |
| resource.unload(); |
| try { |
| resource.load(Collections.EMPTY_MAP); |
| } catch (IOException exception) { |
| EditorPlugin.getDefault().getLog().log( |
| new Status(IStatus.ERROR, EditorPlugin |
| .getPluginId(), IStatus.OK, exception |
| .getMessage() == null ? "" : exception //$NON-NLS-1$ |
| .getMessage(), exception)); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * TODO this is used only for testing. Should be able to remove if we |
| * go to true automated UI testing |
| * @param pageID |
| */ |
| public void setActiveEditorPage(String pageID) { |
| if (pageID.equals(PageflowEditor.PAGE_ID)) { |
| setActivePage(pageflowPageID); |
| } else if (pageID.equals(ManagedBeanPage.PAGE_ID)) { |
| setActivePage(managedBeanPageID); |
| } else if (pageID.equals(ComponentsPage.PAGE_ID)) { |
| setActivePage(componentsPageID); |
| } else if (pageID.equals(OthersPage.PAGE_ID)) { |
| setActivePage(othersPageID); |
| } else if (pageID.equals(SOURCE_PAGE_ID)) { |
| setActivePage(sourcePageId); |
| } |
| } |
| |
| private boolean matches(IEditorInput input) { |
| final IResource file = (IResource) input.getAdapter(IResource.class); |
| boolean hasWebFacet = false; |
| boolean hasJSFFacet = false; |
| |
| if (file != null) { |
| final IProject project = file.getProject(); |
| |
| if (project != null) { |
| try { |
| final IFacetedProject facetedProject = ProjectFacetsManager |
| .create(project); |
| |
| if (facetedProject != null) { |
| final Set facets = facetedProject.getProjectFacets(); |
| |
| for (final Iterator it = facets.iterator(); it |
| .hasNext();) { |
| final IProjectFacetVersion version = (IProjectFacetVersion) it |
| .next(); |
| |
| IProjectFacet facet = version.getProjectFacet(); |
| if (IJSFCoreConstants.JSF_CORE_FACET_ID.equals(facet.getId())) { |
| hasJSFFacet = true; |
| } else if ("jst.web".equals(facet.getId())) { //$NON-NLS-1$ |
| hasWebFacet = true; |
| } |
| } |
| } |
| } catch (CoreException ex) { |
| EditorPlugin.getDefault().getLog().log( |
| new Status(IStatus.ERROR, EditorPlugin |
| .getPluginId(), IStatus.OK, |
| ex.getMessage() == null ? "" : ex //$NON-NLS-1$ |
| .getMessage(), ex)); |
| } |
| } |
| } |
| |
| return hasWebFacet && hasJSFFacet; |
| } |
| |
| /** |
| * DANGER! This call is for testing only! Should not be used, |
| * even internally, by production code. |
| * @param timeoutMs the time to wait in milliseconds |
| * @throws InterruptedException |
| */ |
| public void doPageLoad(long timeoutMs) throws InterruptedException |
| { |
| _modelLoader.waitForLoad(timeoutMs); |
| _addPagesTask.doRun(_modelLoader.getEdit()); |
| } |
| } |