/******************************************************************************
* 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;
}
}