/******************************************************************************
* Copyright (c) 2010-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.services.wizards;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.CheckboxCellEditor;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TableLayout;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.wst.xml.ui.internal.properties.StringComboBoxCellEditor;
import com.ebmwebsourcing.petals.services.PetalsServicesPlugin;
import com.ebmwebsourcing.petals.services.wizards.beans.SaImportBean;
import com.ebmwebsourcing.petals.services.wizards.beans.ServiceImportBean;
import com.ebmwebsourcing.petals.services.wizards.beans.SuImportBean;
/**
* A page that shows a tree and widgets to create SU and SA projects.
* @author Vincent Zurczak - EBM WebSourcing
*/
public abstract class AbstractPetalsServiceCreationWizardPage extends WizardPage {
private static final String COMPONENT_NAME = "Component";
private static final String PROJECT_NAME = "Project Name";
private static final String COMPONENT_VERSION = "Version";
private static final String CREATE = "Create";
private final static String OVERWRITE = "Overwrite";
/**
* The default array of versions, to use to not make the combo editor crash.
*/
private final static String[] DEFAULT_VERSIONS = new String[] { "" };
private Image suImg, saImg;
private Image checked, unchecked;
private String projectLocation;
private boolean complete = false;
private boolean isAtDefaultLocation = true;
private TreeViewer viewer;
private final List<SaImportBean> importsBeans = new ArrayList<SaImportBean> ();
/**
* Constructor.
* @param title the page title
* @param description the page description
*/
public AbstractPetalsServiceCreationWizardPage( String title, String description ) {
super( title );
setTitle( title ); //NON-NLS-1
setDescription( description ); //NON-NLS-1
try {
ImageDescriptor desc = PetalsServicesPlugin.getImageDescriptor( "icons/obj16/service_unit.png" );
if( desc != null )
this.suImg = desc.createImage();
desc = PetalsServicesPlugin.getImageDescriptor( "icons/obj16/service_assembly.png" );
if( desc != null )
this.saImg = desc.createImage();
desc = PetalsServicesPlugin.getImageDescriptor( "icons/obj16/checked.gif" );
if( desc != null )
this.checked = desc.createImage();
desc = PetalsServicesPlugin.getImageDescriptor( "icons/obj16/unchecked.gif" );
if( desc != null )
this.unchecked = desc.createImage();
} catch( Exception e ) {
PetalsServicesPlugin.log( e, IStatus.WARNING );
}
}
/*
* (non-Javadoc)
* @see org.eclipse.jface.dialogs.IDialogPage
* #createControl(org.eclipse.swt.widgets.Composite)
*/
@Override
@SuppressWarnings( "restriction" )
public void createControl( Composite parent ) {
Composite container = new Composite( parent, SWT.NONE );
container.setLayout( new GridLayout ());
container.setLayoutData( new GridData( GridData.FILL_BOTH ));
// Before the workspace location
createWidgetsBeforeProjectLocation( container );
// Workspace location
final Button useDefaultLocButton = new Button( container, SWT.CHECK );
useDefaultLocButton.setText( "Create the project(s) in the default location" );
GridData layoutData = new GridData ();
layoutData.verticalIndent = 17;
useDefaultLocButton.setLayoutData( layoutData );
Composite locContainer = new Composite( container, SWT.NONE );
GridLayout layout = new GridLayout( 3, false );
layout.marginHeight = layout.marginWidth = 0;
locContainer.setLayout( layout );
locContainer.setLayoutData( new GridData( GridData.FILL_HORIZONTAL ));
final Label locLabel = new Label( locContainer, SWT.NONE );
locLabel.setText( "Project(s) location:" );
final Text projectLocationText = new Text( locContainer, SWT.SINGLE | SWT.BORDER );
projectLocationText.setLayoutData( new GridData( GridData.FILL_HORIZONTAL ));
projectLocationText.setText( ResourcesPlugin.getWorkspace().getRoot().getLocation().toOSString());
projectLocationText.addModifyListener(new ModifyListener() {
@Override
public void modifyText(ModifyEvent e) {
AbstractPetalsServiceCreationWizardPage.this.projectLocation = projectLocationText.getText();
validate();
}
});
final Button browseButton = new Button( locContainer, SWT.PUSH );
browseButton.setText( "Browse..." );
browseButton.addSelectionListener( new SelectionAdapter () {
@Override
public void widgetSelected( SelectionEvent e ) {
String location = new DirectoryDialog( getShell()).open();
if( location != null )
projectLocationText.setText( location );
}
});
useDefaultLocButton.setSelection( this.isAtDefaultLocation );
useDefaultLocButton.addSelectionListener( new SelectionAdapter () {
@Override
public void widgetSelected( SelectionEvent e ) {
AbstractPetalsServiceCreationWizardPage.this.isAtDefaultLocation =
useDefaultLocButton.getSelection();
boolean use = ! AbstractPetalsServiceCreationWizardPage.this.isAtDefaultLocation;
locLabel.setEnabled( use );
projectLocationText.setEnabled( use );
browseButton.setEnabled( use );
projectLocationText.setFocus();
}
});
// List of projects to create
Label l = new Label( container, SWT.NONE );
l.setText( "Specify the properties of the target project(s)." );
layoutData = new GridData();
layoutData.verticalIndent = 17;
l.setLayoutData( layoutData );
Tree tree = new Tree( container, SWT.FULL_SELECTION | SWT.BORDER | SWT.SINGLE );
layoutData = new GridData( GridData.FILL_BOTH );
layoutData.heightHint = 90;
tree.setLayoutData( layoutData );
tree.setHeaderVisible( true );
tree.setLinesVisible( true );
TableLayout tlayout = new TableLayout();
tlayout.addColumnData( new ColumnWeightData( 40, 150, false ));
tlayout.addColumnData( new ColumnWeightData( 10, 10, false ));
tlayout.addColumnData( new ColumnWeightData( 10, 15, false ));
tlayout.addColumnData( new ColumnWeightData( 20, 80, true ));
tlayout.addColumnData( new ColumnWeightData( 20, 60, true ));
tree.setLayout( tlayout );
TreeColumn column = new TreeColumn( tree, SWT.LEFT );
column.setText( PROJECT_NAME );
column = new TreeColumn( tree, SWT.CENTER );
column.setText( CREATE );
column = new TreeColumn( tree, SWT.CENTER );
column.setText( OVERWRITE );
column = new TreeColumn( tree, SWT.LEFT );
column.setText( COMPONENT_NAME );
column = new TreeColumn( tree, SWT.CENTER );
column.setText( COMPONENT_VERSION );
// Create the viewer
this.viewer = new TreeViewer( tree );
// Define its content provider
this.viewer.setContentProvider( new ITreeContentProvider() {
@Override
public Object[] getChildren( Object parentElement ) {
Object[] children = new Object[ 0 ];
if( parentElement instanceof SaImportBean ) {
children = new Object[((SaImportBean) parentElement).countSuBeans()];
children = ((SaImportBean) parentElement).getSuBeans().toArray( children );
}
return children;
}
@Override
public Object getParent( Object element ) {
return null;
}
@Override
public boolean hasChildren( Object element ) {
boolean hasChildren = false;
if( element instanceof SaImportBean ) {
hasChildren = ((SaImportBean) element).countSuBeans() > 0;
}
return hasChildren;
}
@Override
public Object[] getElements( Object inputElement ) {
Object[] result = new Object[ ((Collection<?>) inputElement).size()];
return ((Collection<?>) inputElement).toArray( result );
}
@Override
public void dispose() {
// nothing
}
@Override
public void inputChanged( Viewer viewer, Object oldInput, Object newInput ) {
// nothing
}
});
// Define its label provider
this.viewer.setLabelProvider( new ITableLabelProvider() {
@Override
public Image getColumnImage( Object element, int columnIndex ) {
Image result = null;
switch( columnIndex ) {
case 0:
if( element instanceof SaImportBean )
result = AbstractPetalsServiceCreationWizardPage.this.saImg;
else if( element instanceof SuImportBean )
result = AbstractPetalsServiceCreationWizardPage.this.suImg;
break;
case 1:
if( element instanceof ServiceImportBean ) {
result = ((ServiceImportBean) element).isToCreate() ?
AbstractPetalsServiceCreationWizardPage.this.checked
: AbstractPetalsServiceCreationWizardPage.this.unchecked;
}
break;
case 2:
if( element instanceof ServiceImportBean ) {
result = ((ServiceImportBean) element).isToOverwrite() ?
AbstractPetalsServiceCreationWizardPage.this.checked
: AbstractPetalsServiceCreationWizardPage.this.unchecked;
}
break;
}
return result;
}
@Override
public String getColumnText( Object element, int columnIndex ) {
String result = "";
switch( columnIndex ) {
case 0:
if( element instanceof ServiceImportBean )
result = ((ServiceImportBean) element).getProjectName();
break;
case 1:
case 2:
break;
case 3:
if( element instanceof SuImportBean )
result = ((SuImportBean) element).getComponentName();
break;
case 4:
if( element instanceof SuImportBean )
result = ((SuImportBean) element).getComponentVersion();
break;
}
return result;
}
@Override
public void addListener( ILabelProviderListener listener ) {
// nothing
}
@Override
public void dispose() {
// nothing
}
@Override
public boolean isLabelProperty( Object element, String property ) {
return false;
}
@Override
public void removeListener( ILabelProviderListener listener ) {
// nothing
}
});
// Define its sorter
this.viewer.setSorter( new ViewerSorter() {
@Override
public int compare( Viewer viewer, Object e1, Object e2 ) {
if( e1 instanceof ServiceImportBean && e2 instanceof ServiceImportBean ) {
String n1 = ((ServiceImportBean) e1).getProjectName();
if( n1 == null )
n1 = "";
String n2 = ((ServiceImportBean) e2).getProjectName();
if( n2 == null )
n2 = "";
return n1.compareTo( n2 );
}
return super.compare( viewer, e1, e2 );
}
});
// Create the editor for the versions (it must be done before creating the cell modifiers).
// This is done this way to allow the editor to store and retrieve custom values (i.e. versions
// that are not registered but manually entered by the user).
final StringComboBoxCellEditor comboEditor = new StringComboBoxCellEditor( tree, DEFAULT_VERSIONS, SWT.DROP_DOWN );
this.viewer.addSelectionChangedListener( new ISelectionChangedListener() {
@Override
public void selectionChanged( SelectionChangedEvent event ) {
if( ! event.getSelection().isEmpty()) {
Object o = ((IStructuredSelection) event.getSelection()).getFirstElement();
if( o instanceof SuImportBean ) {
// If there is a custom value, add it in the list
SuImportBean suBean = (SuImportBean) o;
List<String> versions = Arrays.asList( suBean.getSupportedVersions());
versions = new ArrayList<String>( versions );
if( ! versions.contains( suBean.getComponentVersion()))
versions.add( suBean.getComponentVersion());
String[] items = new String[ versions.size()];
comboEditor.setItems( versions.toArray( items ));
}
else if( o instanceof SaImportBean )
comboEditor.setItems( DEFAULT_VERSIONS );
}
}
});
// Define its cell modifier
this.viewer.setCellModifier( new ICellModifier() {
@Override
public void modify( Object element, String property, Object value ) {
TreeItem tableItem = (TreeItem) element;
if( PROJECT_NAME.equals( property )) {
ServiceImportBean bean = (ServiceImportBean) tableItem.getData();
bean.setProjectName((String) value);
AbstractPetalsServiceCreationWizardPage.this.viewer.refresh( bean );
validate();
} else if( CREATE.equals( property )) {
ServiceImportBean bean = (ServiceImportBean) tableItem.getData();
bean.setToCreate((Boolean) value);
AbstractPetalsServiceCreationWizardPage.this.viewer.refresh( bean );
validate();
} else if( OVERWRITE.equals( property )) {
ServiceImportBean bean = (ServiceImportBean) tableItem.getData();
bean.setOverwrite((Boolean) value);
AbstractPetalsServiceCreationWizardPage.this.viewer.refresh( bean );
validate();
} else if( tableItem.getData() instanceof SuImportBean ) {
if( COMPONENT_NAME.equals( property )) {
SuImportBean bean = (SuImportBean) tableItem.getData();
bean.setComponentName((String) value);
AbstractPetalsServiceCreationWizardPage.this.viewer.refresh( bean );
validate();
} else if( COMPONENT_VERSION.equals( property )) {
SuImportBean bean = (SuImportBean) tableItem.getData();
bean.setComponentVersion((String) value);
AbstractPetalsServiceCreationWizardPage.this.viewer.refresh( bean );
validate();
}
}
}
@Override
public Object getValue( Object element, String property ) {
Object value = null;
if( PROJECT_NAME.equals( property ))
value = ((ServiceImportBean) element).getProjectName();
else if( CREATE.equals( property ))
value = Boolean.valueOf(((ServiceImportBean) element).isToCreate());
else if( OVERWRITE.equals( property ))
value = ((ServiceImportBean) element).isToOverwrite();
else if( element instanceof SuImportBean ) {
SuImportBean bean = (SuImportBean) element;
if( COMPONENT_NAME.equals( property ))
value = bean.getComponentName();
else if( COMPONENT_VERSION.equals( property ))
value = bean.getComponentVersion();
}
return value;
}
@Override
public boolean canModify( Object element, String property ) {
boolean canModify = true;
if( element instanceof SaImportBean ) {
canModify = PROJECT_NAME.equals( property ) ||
CREATE.equals( property ) ||
OVERWRITE.equals( property );
}
return canModify;
}
});
// End up with the viewer properties
this.viewer.setColumnProperties( new String[] {
PROJECT_NAME, CREATE, OVERWRITE, COMPONENT_NAME, COMPONENT_VERSION });
this.viewer.setCellEditors( new CellEditor[] {
new TextCellEditor( tree ),
new CheckboxCellEditor( tree ),
new CheckboxCellEditor( tree ),
new TextCellEditor( tree, SWT.READ_ONLY ),
comboEditor });
// Last steps
if( ! this.importsBeans.isEmpty()) {
this.viewer.setInput( this.importsBeans );
this.viewer.expandAll();
}
useDefaultLocButton.notifyListeners( SWT.Selection, new Event());
tree.notifyListeners( SWT.Selection, new Event());
validate();
setErrorMessage( null );
setControl( container );
// Force the shell size
Point size = getShell().computeSize( 700, 600 );
getShell().setSize( size );
Rectangle rect = Display.getCurrent().getBounds();
getShell().setLocation((rect.width - size.x) / 2, (rect.height - size.y) / 2);
}
/**
* Validates the page entries.
* @return true if the validation succeeded, false otherwise
*/
protected boolean validate() {
// Project location
if( ! this.isAtDefaultLocation ) {
if( this.projectLocation.trim().length() == 0 ) {
setErrorMessage( "The project(s) location is not specified." );
setPageComplete( false );
return false;
}
if( ! new File( this.projectLocation ).exists()) {
setErrorMessage( "The project(s) location is not a valid directory." );
setPageComplete( false );
return false;
}
}
// Validate the projects
Set<String> names = new HashSet<String> ();
for( SaImportBean saBean : this.importsBeans ) {
if( ! validateServiceProject( saBean ))
return false;
for( SuImportBean suBean : saBean.getSuBeans()) {
if( ! validateServiceProject( suBean ))
return false;
if( suBean.isToCreate()
&& ! isNameUnique( suBean.getProjectName(), names ))
return false;
}
if( saBean.isToCreate()
&& ! isNameUnique( saBean.getProjectName(), names ))
return false;
}
setErrorMessage( null );
setPageComplete( true );
return true;
}
/**
* Tests the uniqueness of the project names.
* @param name the name to check
* @param names the stores names
* @return true if this name is unique, false otherwise
*/
private boolean isNameUnique( String name, Set<String> names ) {
boolean result = true;
if( names.contains( name )) {
setErrorMessage( "There is a conflict in the project names. " + name + " is already in the list." );
result = false;
} else {
names.add( name );
}
setPageComplete( result );
return result;
}
/**
* Validates the creation of a Petals service project.
* @param serviceImportBean a service import bean
* @return true if a project can be created from this bean, false otherwise
*/
private boolean validateServiceProject( ServiceImportBean serviceImportBean ) {
boolean valid = true;
if( serviceImportBean.isToCreate()) {
IWorkspaceRoot wr = ResourcesPlugin.getWorkspace().getRoot();
// Existing project
IProject p = wr.getProject( serviceImportBean.getProjectName());
if( p.exists() && serviceImportBean.isToCreate() && ! serviceImportBean.isToOverwrite()) {
setErrorMessage( "The project " + serviceImportBean.getProjectName() + " already exists." );
setPageComplete( false );
valid = false;
}
// Existing target location ?
if( ! this.isAtDefaultLocation ) {
File f = new File( this.projectLocation, serviceImportBean.getProjectName());
if( f.exists()) {
setErrorMessage( "A file already exists at the target location for " + serviceImportBean.getProjectName() + "." );
setPageComplete( false );
valid = false;
}
}
}
return valid;
}
/*
* (non-Javadoc)
* @see org.eclipse.jface.wizard.WizardPage
* #isPageComplete()
*/
@Override
public boolean isPageComplete() {
return this.complete;
}
/*
* (non-Javadoc)
* @see org.eclipse.jface.wizard.WizardPage
* #setPageComplete(boolean)
*/
@Override
public void setPageComplete( boolean complete ) {
this.complete = complete;
super.setPageComplete( complete );
}
/**
* @return the importsBeans
*/
public List<SaImportBean> getImportsBeans() {
return this.importsBeans;
}
/**
* Adds import beans in the list managed by this class.
* <p>
* After insertion, the viewer is refreshed and expanded.
* </p>
*
* @param importBeans the import beans to set (may be null)
* @param clearBeforeInsertion true to empty the current list before insertion
*/
public void updateImportBeans( List<SaImportBean> importBeans, boolean clearBeforeInsertion ) {
if( clearBeforeInsertion )
this.importsBeans.clear();
if( importBeans != null )
this.importsBeans.addAll( importBeans );
if( this.viewer != null ) {
this.viewer.setInput( importBeans );
this.viewer.refresh();
this.viewer.expandAll();
}
}
/**
* @return the isAtDefaultLocation
*/
public boolean isAtDefaultLocation() {
return this.isAtDefaultLocation;
}
/**
* @return the projectLocation
*/
public String getProjectLocation() {
return this.projectLocation;
}
/*
* (non-Javadoc)
* @see org.eclipse.jface.dialogs.DialogPage#dispose()
*/
@Override
public void dispose() {
if( this.viewer.getLabelProvider() != null )
this.viewer.getLabelProvider().dispose();
if( this.suImg != null )
this.suImg.dispose();
if( this.saImg != null )
this.saImg.dispose();
if( this.checked != null )
this.checked.dispose();
if( this.unchecked != null )
this.unchecked.dispose();
releaseResources();
super.dispose();
}
/**
* Releases additional resources.
*/
protected abstract void releaseResources();
/**
* Creates the widgets to show before the project location UI part.
* @param container
*/
protected abstract void createWidgetsBeforeProjectLocation( Composite container );
}