/****************************************************************************** * Copyright (c) 2009-2013, Linagora * * 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: * Linagora - initial API and implementation *******************************************************************************/ package com.ebmwebsourcing.petals.common.internal.formeditor; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.EventObject; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.core.databinding.DataBindingContext; 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.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtensionRegistry; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.emf.common.command.BasicCommandStack; import org.eclipse.emf.common.command.CommandStack; import org.eclipse.emf.common.command.CommandStackListener; 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.ecore.resource.impl.ResourceSetImpl; import org.eclipse.emf.edit.domain.EditingDomain; import org.eclipse.emf.edit.ui.action.EditingDomainActionBarContributor; import org.eclipse.emf.transaction.TransactionalEditingDomain; import org.eclipse.jface.action.IStatusLineManager; import org.eclipse.jface.dialogs.IMessageProvider; import org.eclipse.jface.dialogs.ProgressMonitorDialog; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IEditorSite; import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.actions.WorkspaceModifyOperation; import org.eclipse.ui.forms.widgets.FormToolkit; import org.eclipse.ui.forms.widgets.ScrolledForm; import org.eclipse.ui.part.EditorPart; import org.eclipse.ui.part.FileEditorInput; import com.ebmwebsourcing.petals.common.internal.PetalsCommonPlugin; import com.ebmwebsourcing.petals.common.internal.provisional.formeditor.AbstractJbiEditorPersonality; import com.ebmwebsourcing.petals.common.internal.provisional.formeditor.ISharedEdition; import com.ebmwebsourcing.petals.common.internal.provisional.utils.PetalsConstants; import com.ebmwebsourcing.petals.common.internal.provisional.utils.StringUtils; import com.sun.java.xml.ns.jbi.DocumentRoot; import com.sun.java.xml.ns.jbi.Jbi; /** * The Petals form editor for JBI descriptors. */ public class JbiFormEditor extends EditorPart implements IEditorPart, ISelectionProvider, ISharedEdition { private static final String EXTENSION_POINT = "com.ebmwebsourcing.petals.common.formeditor"; private DataBindingContext dbc; private Jbi jbiModel; private TransactionalEditingDomain editDomain; private FormToolkit toolkit; private IFile editedFile; private IResourceChangeListener workspaceListener; private AbstractJbiEditorPersonality personality; private Image editorImage; private ScrolledForm mainForm; private final Set<ISelectionChangedListener> selectionListeners = new HashSet<ISelectionChangedListener>(); private ISelection selection; /** * @return the editor personality */ private AbstractJbiEditorPersonality getPersonality() { if( this.personality == null && this.editedFile != null) { // Look into the extensions IExtensionRegistry reg = Platform.getExtensionRegistry(); List<AbstractJbiEditorPersonality> personalities = new ArrayList<AbstractJbiEditorPersonality> (); IConfigurationElement[] extensions = reg.getConfigurationElementsFor( EXTENSION_POINT ); for( IConfigurationElement elt : extensions ) { String className = elt.getAttribute( "class" ); if( StringUtils.isEmpty( className )) { PetalsCommonPlugin.log( "No personality was found for " + elt.getContributor().getName(), IStatus.ERROR ); continue; } try { AbstractJbiEditorPersonality pers = (AbstractJbiEditorPersonality) elt.createExecutableExtension( "class" ); personalities.add( pers ); } catch( CoreException e ) { PetalsCommonPlugin.log( "A JBI personality could not be instantiated - " + className, IStatus.ERROR ); } } for( AbstractJbiEditorPersonality pers : personalities ) { if( pers.matchesPersonality( this.jbiModel, this.editedFile )) { this.personality = pers; break; } } if( this.personality == null ) this.personality = new JbiDefaultPersonality(); } return this.personality; } /** * Loads the DOM model. * <p> * If this model was already loaded, we reuse it. Otherwise, we make it loaded. * </p> */ private void loadSharedModel() throws IOException { try { // Get the edited file if (getEditorInput() instanceof IFileEditorInput) { this.editedFile = ((IFileEditorInput) getEditorInput()).getFile(); if( !this.editedFile.exists()) throw new FileNotFoundException( this.editedFile.getLocation() + " does not exist."); if( ! this.editedFile.isSynchronized( IResource.DEPTH_ONE)) this.editedFile.refreshLocal( IResource.DEPTH_ONE, null ); } else { // Don't load anything return; } ResourceSet resourceSet = new ResourceSetImpl(); this.editDomain = TransactionalEditingDomain.Factory.INSTANCE.createEditingDomain( resourceSet ); URI resourceUri = URI.createPlatformResourceURI( this.editedFile.getFullPath().toString(), true ); Resource resource = resourceSet.getResource(resourceUri, true); resource.load( resourceSet.getLoadOptions()); this.jbiModel = ((DocumentRoot) resource.getContents().get(0)).getJbi(); this.editDomain.getCommandStack().addCommandStackListener( new CommandStackListener() { @Override public void commandStackChanged( final EventObject event ) { Display.getDefault().asyncExec( new Runnable() { @Override public void run() { firePropertyChange( IEditorPart.PROP_DIRTY ); } }); } }); } catch( CoreException e ) { PetalsCommonPlugin.log(e, IStatus.WARNING); } } /** * Initializes the resource listener to check markers. */ private void initializeResourceListener() { // No need to monitor changes if the file is not in the workspace if( this.editedFile == null ) return; // The resource delta visitor final IResourceDeltaVisitor visitor = new IResourceDeltaVisitor() { @Override public boolean visit( final IResourceDelta delta ) throws CoreException { // Check for changes. if( delta.getResource().equals( JbiFormEditor.this.editedFile )) { // Deleted or renamed if( delta.getKind() == IResourceDelta.REMOVED ) { if( delta.getMovedToPath() == null ) { // Deleted getSite().getShell().getDisplay().asyncExec( new Runnable() { @Override public void run() { getSite().getPage().closeEditor( JbiFormEditor.this, false ); } }); } else { // Renamed? if( delta.getMovedToPath() != null ) { Display.getDefault().asyncExec( new Runnable() { @Override public void run() { setPartName( delta.getMovedToPath().lastSegment()); } }); } } } // Marker changes else if((delta.getFlags() & IResourceDelta.MARKERS) != 0 ) refreshMarkers(); return false; } // Keep on checking on, only if the resource is an ancestor of the edited file. return delta.getResource().getFullPath().isPrefixOf( JbiFormEditor.this.editedFile.getFullPath()); } }; // The resource change listener this.workspaceListener = new IResourceChangeListener() { @Override public void resourceChanged( IResourceChangeEvent event ) { try { event.getDelta().accept( visitor ); } catch( CoreException e ) { PetalsCommonPlugin.log( e, IStatus.ERROR ); } } }; // Register it ResourcesPlugin.getWorkspace().addResourceChangeListener( this.workspaceListener, IResourceChangeEvent.POST_BUILD ); } /** * Refreshes the markers * @param markerDeltas */ private void refreshMarkers() { if( this.editedFile == null || ! this.editedFile.exists()) return; try { // Prepare the messages final Set<String> warningMessages = new HashSet<String> (); final Set<String> errorMessages = new HashSet<String> (); IMarker[] markers = this.editedFile.findMarkers( PetalsConstants.MARKER_ID_JBI_XML, true, IResource.DEPTH_ZERO ); if( markers != null ) { for( IMarker marker : markers ) { int severity = marker.getAttribute( IMarker.SEVERITY, -1 ); if( severity == IMarker.SEVERITY_ERROR ) errorMessages.add( marker.getAttribute( IMarker.MESSAGE, "" )); else if( severity == IMarker.SEVERITY_WARNING ) warningMessages.add( marker.getAttribute( IMarker.MESSAGE, "" )); } } // Update the message manager Display.getDefault().asyncExec( new Runnable() { @Override public void run() { if( JbiFormEditor.this.mainForm.isDisposed()) return; JbiFormEditor.this.mainForm.getMessageManager().removeAllMessages(); int i = 0; for( String msg : errorMessages ) JbiFormEditor.this.mainForm.getMessageManager().addMessage( "error" + i++, msg, null, IMessageProvider.ERROR ); for( String msg : warningMessages ) JbiFormEditor.this.mainForm.getMessageManager().addMessage( "warning" + i++, msg, null, IMessageProvider.WARNING ); } }); } catch( CoreException e ) { PetalsCommonPlugin.log( e, IStatus.ERROR ); } } /** * This is called during startup. */ @Override public void init( IEditorSite site, IEditorInput editorInput ) { setSite( site ); setInputWithNotify( editorInput ); if (editorInput instanceof FileEditorInput) setPartName(((FileEditorInput) editorInput).getFile().getProject().getName()); else setPartName( editorInput.getName()); site.setSelectionProvider( this ); try { loadSharedModel(); initializeResourceListener(); this.dbc = new DataBindingContext(); } catch( Exception ex ) { PetalsCommonPlugin.log( ex, IStatus.ERROR ); } } /* * (non-Javadoc) * @see org.eclipse.ui.part.EditorPart * #doSave(org.eclipse.core.runtime.IProgressMonitor) */ @Override 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() { @Override public void execute( IProgressMonitor monitor ) { // Delegate the save part to the personality handler getPersonality().saveModel( JbiFormEditor.this.jbiModel, JbiFormEditor.this.editedFile, JbiFormEditor.this.editDomain ); } }; try { new ProgressMonitorDialog( getSite().getShell()).run( true, false, operation ); ((BasicCommandStack) getEditingDomain().getCommandStack()).saveIsDone(); firePropertyChange( IEditorPart.PROP_DIRTY ); } catch( Exception exception ) { PetalsCommonPlugin.log( exception, IStatus.ERROR ); } } /* * (non-Javadoc) * @see org.eclipse.ui.part.EditorPart * #doSaveAs() */ @Override public void doSaveAs() { // Not supported } /* * (non-Javadoc) * @see org.eclipse.ui.part.EditorPart * #isSaveAsAllowed() */ @Override public boolean isSaveAsAllowed() { return false; } /* * (non-Javadoc) * @see org.eclipse.jface.viewers.ISelectionProvider * #addSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener) */ @Override public void addSelectionChangedListener(ISelectionChangedListener listener) { this.selectionListeners.add(listener); } /* * (non-Javadoc) * @see org.eclipse.jface.viewers.ISelectionProvider * #getSelection() */ @Override public ISelection getSelection() { return this.selection; } /* * (non-Javadoc) * @see org.eclipse.jface.viewers.ISelectionProvider * #removeSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener) */ @Override public void removeSelectionChangedListener( ISelectionChangedListener listener ) { this.selectionListeners.remove(listener); } /* * (non-Javadoc) * @see org.eclipse.jface.viewers.ISelectionProvider * #setSelection(org.eclipse.jface.viewers.ISelection) */ @Override public void setSelection(ISelection selection) { // Update the selection this.selection = selection; for (ISelectionChangedListener listener : this.selectionListeners) { listener.selectionChanged(new SelectionChangedEvent(this, selection)); } // Update the status line try { IStatusLineManager manager = getEditorSite().getActionBars().getStatusLineManager(); if (selection instanceof IStructuredSelection) { IStructuredSelection sse = (IStructuredSelection) selection; switch (sse.size()) { case 0: manager.setMessage(""); break; case 1: ILabelProvider lp = getStatusLineLabelProvider(); if (lp != null) { String msg = lp.getText(sse.getFirstElement()); Image img = lp.getImage(sse.getFirstElement()); manager.setMessage(img, msg); } break; default: manager.setMessage(sse.size() + " selected elements"); break; } } } catch( Exception e ) { PetalsCommonPlugin.log(e, IStatus.WARNING); } } /** * @return a label provider for the status line manager */ public ILabelProvider getStatusLineLabelProvider() { AbstractJbiEditorPersonality pers = getPersonality(); return pers != null ? pers.getStatusLineLabelProvider() : null; } /** * @return */ public EditingDomainActionBarContributor getActionBarContributor() { return (EditingDomainActionBarContributor) getEditorSite().getActionBarContributor(); } /* * (non-Javadoc) * @see org.eclipse.ui.part.WorkbenchPart * #dispose() */ @Override public void dispose() { if( this.editDomain != null ) this.editDomain.dispose(); if( this.dbc != null ) this.dbc.dispose(); if( this.toolkit != null ) this.toolkit.dispose(); if( this.editorImage != null && ! this.editorImage.isDisposed()) this.editorImage.dispose(); ResourcesPlugin.getWorkspace().removeResourceChangeListener( this.workspaceListener ); super.dispose(); } /* * (non-Javadoc) * @see org.eclipse.ui.part.EditorPart * #isDirty() */ @Override public boolean isDirty() { CommandStack commandStack = getEditingDomain().getCommandStack(); return commandStack != null ? ((BasicCommandStack) commandStack).isSaveNeeded() : false; } /* * (non-Javadoc) * @see org.eclipse.ui.part.WorkbenchPart * #createPartControl(org.eclipse.swt.widgets.Composite) */ @Override public void createPartControl( Composite parent ) { AbstractJbiEditorPersonality pers = getPersonality(); this.toolkit = new FormToolkit( getSite().getShell().getDisplay()); this.mainForm = this.toolkit.createScrolledForm( parent ); this.mainForm.setText( pers != null ? pers.getTitle() : "Title" ); this.editorImage = pers != null ? pers.getTitleImage() : null; this.mainForm.setImage( this.editorImage ); this.toolkit.decorateFormHeading( this.mainForm.getForm()); this.toolkit.paintBordersFor( this.mainForm.getBody()); GridLayout layout = new GridLayout(); layout.marginBottom = 4; this.mainForm.getBody().setLayout( layout ); this.mainForm.setLayoutData ( new GridData( GridData.FILL_BOTH )); if( pers != null ) pers.createControl( this.mainForm.getBody(), this ); refreshMarkers(); } /* * (non-Javadoc) * @see org.eclipse.ui.part.WorkbenchPart * #setFocus() */ @Override public void setFocus() { // nothing } /* * (non-Javadoc) * @see org.eclipse.emf.edit.domain.IEditingDomainProvider * #getEditingDomain() */ @Override public EditingDomain getEditingDomain() { return this.editDomain; } /* * (non-Javadoc) * @see com.ebmwebsourcing.petals.services.jbi.editor.ISharedEdition * #getFormToolkit() */ @Override public FormToolkit getFormToolkit() { return this.toolkit; } /* * (non-Javadoc) * @see com.ebmwebsourcing.petals.services.jbi.editor.ISharedEdition * #getDataBindingContext() */ @Override public DataBindingContext getDataBindingContext() { return this.dbc; } /* * (non-Javadoc) * @see com.ebmwebsourcing.petals.services.jbi.editor.ISharedEdition * #getJbiModel() */ @Override public Jbi getJbiModel() { return this.jbiModel; } /* * (non-Javadoc) * @see com.ebmwebsourcing.petals.services.jbi.editor.ISharedEdition * #getEditedFile() */ @Override public IFile getEditedFile() { return this.editedFile; } }