/*******************************************************************************
 * Copyright (c) 2005 Actuate Corporation.
 * 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:
 *  Actuate Corporation  - initial API and implementation
 *******************************************************************************/

package org.eclipse.birt.report.designer.data.ui.dataset;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.eclipse.birt.report.designer.data.ui.property.AbstractDescriptionPropertyPage;
import org.eclipse.birt.report.designer.internal.ui.util.ExceptionHandler;
import org.eclipse.birt.report.designer.nls.Messages;
import org.eclipse.birt.report.designer.ui.ReportPlatformUIImages;
import org.eclipse.birt.report.model.api.ColumnHintHandle;
import org.eclipse.birt.report.model.api.DataSetHandle;
import org.eclipse.birt.report.model.api.DesignElementHandle;
import org.eclipse.birt.report.model.api.PropertyHandle;
import org.eclipse.birt.report.model.api.ResultSetColumnHandle;
import org.eclipse.birt.report.model.api.activity.NotificationEvent;
import org.eclipse.birt.report.model.api.activity.SemanticException;
import org.eclipse.birt.report.model.api.core.IStructure;
import org.eclipse.birt.report.model.api.core.Listener;
import org.eclipse.birt.report.model.api.elements.DesignChoiceConstants;
import org.eclipse.birt.report.model.api.elements.structures.ColumnHint;
import org.eclipse.birt.report.model.api.elements.structures.ResultSetColumn;
import org.eclipse.birt.report.model.api.metadata.PropertyValueException;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ComboBoxCellEditor;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MenuAdapter;
import org.eclipse.swt.events.MenuEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
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.Control;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;

/**
 * Property page to edit script dataset column definition.
 *  
 */
public class OutputColumnDefnPage extends AbstractDescriptionPropertyPage
		implements
			Listener
{

	private boolean modelChanged = true;
	private PropertyHandle rsColumns;
	private PropertyHandle columnHints;
	private Map rsColumnMap = new HashMap( );
	private Map columnHintMap = new HashMap( );
	private List columnHandlesList = new ArrayList( );
	
	private OutputColumnTableViewer viewer;
	private static String[] dataTypes = new String[]{
			DesignChoiceConstants.COLUMN_DATA_TYPE_ANY,
			DesignChoiceConstants.COLUMN_DATA_TYPE_DATETIME,
			DesignChoiceConstants.COLUMN_DATA_TYPE_DECIMAL,
			DesignChoiceConstants.COLUMN_DATA_TYPE_FLOAT,
			DesignChoiceConstants.COLUMN_DATA_TYPE_INTEGER,
			DesignChoiceConstants.COLUMN_DATA_TYPE_STRING,
	};

	private static String[] dataTypeDisplayNames = new String[]{
			Messages.getString( "datatypes.any" ), //$NON-NLS-1$
			Messages.getString( "datatypes.dateTime" ), //$NON-NLS-1$
			Messages.getString( "datatypes.decimal" ), //$NON-NLS-1$
			Messages.getString( "datatypes.float" ), //$NON-NLS-1$
			Messages.getString( "datatypes.integer" ), //$NON-NLS-1$
			Messages.getString( "datatypes.string" ) //$NON-NLS-1$
	};

	private static String NAME = "name"; //$NON-NLS-1$
	private static String TYPE = "dataType"; //$NON-NLS-1$
	private static String ALIAS = "alias"; //$NON-NLS-1$
	private static String DISPLAY_NAME = "displayName"; //$NON-NLS-1$
	private static String HELP_TEXT = "helpText"; //$NON-NLS-1$

	private ColumnDefn newDefn = null;

	private static final Image selectorImage = ReportPlatformUIImages.getImage("TableRowSelector" ); //$NON-NLS-1$

	private static String DEFAULT_MESSAGE = Messages.getString( "dataset.editor.outputColumns" );//$NON-NLS-1$
	private static String DUPLICATE_MESSAGE = Messages.getString( "error.duplicateName" ); //$NON-NLS-1$
	private static String BLANK_MESSAGE = Messages.getString( "error.emptyName" ); //$NON-NLS-1$

	/**
	 *  
	 */
	public OutputColumnDefnPage( )
	{
		super( );
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.birt.report.designer.ui.dialogs.properties.AbstractDescriptionPropertyPage#createContents(org.eclipse.swt.widgets.Composite)
	 */
	public Control createContents( Composite parent )
	{
		rsColumns = ( (DataSetHandle) getContainer( ).getModel( ) ).getPropertyHandle( DataSetHandle.RESULT_SET_HINTS_PROP );
		columnHints = ( (DataSetHandle) getContainer( ).getModel( ) ).getPropertyHandle( DataSetHandle.COLUMN_HINTS_PROP );
		createCachedMap( );
		initColumnHandles( );
		
		viewer = new OutputColumnTableViewer( parent, true, true, true );
		TableColumn column = new TableColumn( viewer.getViewer( ).getTable( ),
				SWT.LEFT );
		column.setText( " " ); //$NON-NLS-1$
		column.setResizable( false );
		column.setWidth( 19 );

		column = new TableColumn( viewer.getViewer( ).getTable( ), SWT.LEFT );
		column.setText( Messages.getString( "dataset.editor.title.name" ) ); //$NON-NLS-1$
		column.setWidth( 100 );

		column = new TableColumn( viewer.getViewer( ).getTable( ), SWT.LEFT );
		column.setText( Messages.getString( "dataset.editor.title.type" ) ); //$NON-NLS-1$
		column.setWidth( 100 );

		column = new TableColumn( viewer.getViewer( ).getTable( ), SWT.LEFT );
		column.setText( Messages.getString( "dataset.editor.title.alias" ) ); //$NON-NLS-1$
		column.setWidth( 100 );

		column = new TableColumn( viewer.getViewer( ).getTable( ), SWT.LEFT );
		column.setText( Messages.getString( "dataset.editor.title.displayName" ) ); //$NON-NLS-1$
		column.setWidth( 100 );

		//TODO Add support to helpText after release 1

		//column = new TableColumn( viewer.getViewer( ).getTable( ), SWT.LEFT );
		//column.setText( Messages.getString( "dataset.editor.title.helpText" ) ); //$NON-NLS-1$
		//column.setWidth( 100 );

		viewer.getViewer( )
				.setContentProvider( new IStructuredContentProvider( ) {

					public Object[] getElements( Object inputElement )
					{
						List temp = columnHandlesList;
						ArrayList defns = new ArrayList( );
						
						if ( newDefn == null )
						{
							newDefn = new ColumnDefn( );
						}

						defns.addAll( temp );
						defns.add( newDefn );

						return defns.toArray( );
					}

					public void dispose( )
					{
					}

					public void inputChanged( Viewer viewer, Object oldInput,
							Object newInput )
					{
					}

				} );

		viewer.getViewer( ).setLabelProvider( new ITableLabelProvider( ) {

			public Image getColumnImage( Object element, int columnIndex )
			{
				if ( columnIndex == 0 )
				{
					return selectorImage;
				}
				return null;
			}

			public String getColumnText( Object element, int columnIndex )
			{
				String value = null;
				ColumnDefn defn = null;
				if ( element instanceof ColumnHandles )
				{
					defn = new ColumnDefn( (ColumnHandles) element );
				}
				else if ( element instanceof ColumnDefn )
				{
					defn = (ColumnDefn) element;
				}
				else
				{
					return ""; //$NON-NLS-1$
				}

				switch ( columnIndex )
				{
					case 1 :
					{
						value = defn.getColumnName( );
						break;
					}
					case 2 :
					{
						if ( defn != newDefn )
							value = getTypeDisplayName( defn.getDataType( ) );
						break;
					}
					case 3 :
					{
						value = defn.getAlias( );
						break;
					}
					case 4 :
					{
						value = defn.getDisplayName( );
						break;
					}
					case 5 :
					{
						value = defn.getHelpText( );
						break;
					}
				}

				if ( value == null )
				{
					value = ""; //$NON-NLS-1$
				}
				return value;
			}

			public void addListener( ILabelProviderListener listener )
			{
			}

			public void dispose( )
			{
			}

			public boolean isLabelProperty( Object element, String property )
			{
				return false;
			}

			public void removeListener( ILabelProviderListener listener )
			{
			}
		} );

		viewer.getViewer( ).setInput( columnHandlesList );
		setupEditors( );
		setPageProperties( );
		( (DataSetHandle) getContainer( ).getModel( ) ).addListener( this );
		return viewer.getControl( );
	}

	protected final void setupEditors( )
	{
		CellEditor[] editors = new CellEditor[6];
		editors[1] = new TextCellEditor( viewer.getViewer( ).getTable( ),
				SWT.NONE );
		editors[2] = new ComboBoxCellEditor( viewer.getViewer( ).getTable( ),
				dataTypeDisplayNames,
				SWT.READ_ONLY );
		editors[3] = new TextCellEditor( viewer.getViewer( ).getTable( ),
				SWT.NONE );
		editors[4] = new TextCellEditor( viewer.getViewer( ).getTable( ),
				SWT.NONE );
		editors[5] = new TextCellEditor( viewer.getViewer( ).getTable( ),
				SWT.NONE );

		viewer.getViewer( ).setColumnProperties( new String[]{
				"", //$NON-NLS-1$
				NAME, TYPE, ALIAS, DISPLAY_NAME, HELP_TEXT
		} );

		viewer.getViewer( ).setCellEditors( editors );
		viewer.getViewer( ).setCellModifier( new ICellModifier( ) {

			public boolean canModify( Object element, String property )
			{
				getContainer( ).setMessage( DEFAULT_MESSAGE,
						IMessageProvider.NONE );
				if ( element == newDefn && !property.equals( NAME ) )
					return false;
				else
					return true;
			}

			public Object getValue( Object element, String property )
			{
				ColumnDefn defn = null;
				if ( element instanceof ColumnHandles )
				{
					defn = new ColumnDefn( (ColumnHandles) element );
				}
				else if ( element == newDefn )
				{
					defn = newDefn;
				}
				else
				{
					return ""; //$NON-NLS-1$
				}

				Object value = null;
				try
				{
					value = defn.getProperty( property );
				}
				catch ( Exception e )
				{
					ExceptionHandler.handle( e );
				}

				if ( property.equals( TYPE ) )
				{
					if ( value != null )
					{
						value = new Integer( getTypeIndex( value.toString( ) ) );
					}
					else
					{
						value = new Integer( -1 );
					}
				}

				if ( value == null )
				{
					value = ""; //$NON-NLS-1$
				}

				return value;
			}

			public void modify( Object element, String property, Object value )
			{
				Object actualElement = ( (TableItem) element ).getData( );
				Object finalValue = value;

				ColumnDefn defn = null;
				if ( actualElement instanceof ColumnHandles )
				{
					defn = new ColumnDefn( (ColumnHandles) actualElement );
				}
				else if ( actualElement == newDefn )
				{
					defn = newDefn;
				}
				else
				{
					return;
				}
				
				if ( property.equals( TYPE ) )
				{
					finalValue = getTypeString( ( (Integer) value ).intValue( ) );
				}

				if ( property.equals( NAME ) )
				{
					if ( finalValue == null || finalValue.equals( "" ) ) //$NON-NLS-1$
					{
						getContainer( ).setMessage( BLANK_MESSAGE,
								IMessageProvider.ERROR );
						return;
					}
					if ( isDuplicated( finalValue, actualElement ) )
					{
						getContainer( ).setMessage( DUPLICATE_MESSAGE,
								IMessageProvider.ERROR );
						return;
					}
					updateNewDefnMap( defn.getColumnName( ), finalValue );
				}
				defn.setProperty( property, finalValue );
	
				getContainer( ).setMessage( DEFAULT_MESSAGE,
						IMessageProvider.NONE );

				try
				{
					if ( actualElement == newDefn )
					{
						if ( newDefn.getColumnName( ) != null
								&& newDefn.getColumnName( ).trim( ).length( ) > 0 )
						{
							if ( newDefn.getDataType( ) == null
									|| newDefn.getDataType( ).trim( ).length( ) == 0 )
							{
								newDefn.setDataType( DesignChoiceConstants.COLUMN_DATA_TYPE_ANY );
							}
							addNewDefn( newDefn );
							newDefn = null;
							setPageProperties( );
							viewer.getViewer( ).refresh( );
						}
					}
					viewer.getViewer( ).update( actualElement, null );
					updateMessage( );
				}
				catch ( Exception ex )
				{
					ExceptionHandler.handle( ex );
				}
			}

		} );
	}

	private boolean isDuplicated( Object columnName, Object data )
	{
		if ( columnHintMap.containsKey( columnName ) )
		{
			if ( data instanceof ColumnDefn )
			{
				return true;
			}
			return columnHintMap.get( columnName ) != ( (ColumnHandles) data ).getColumnHintHandle().getStructure();
		}
		return false;
	}

	private void addNewDefn( ColumnDefn defn ) throws SemanticException
	{
		String name = defn.getColumnName( );
		if ( rsColumnMap != null )
		{
			if ( rsColumnMap.get( name ) != null )
			{
				name = getUniqueName( );
				defn.setColumnName( name );
			}
			ResultSetColumnHandle rsHandle;
			if ( rsColumns != null && columnHints != null )
			{
				rsHandle = (ResultSetColumnHandle) rsColumns.addItem( defn.getResultSetColumn( ) );
				ColumnHintHandle columnHandle = (ColumnHintHandle) columnHints.addItem( defn.getColumnHint( ) );
				rsColumnMap.put( name, defn.getResultSetColumn( ) );
				columnHintMap.put( name, defn.getColumnHint( ) );
				columnHandlesList.add( new ColumnHandles( rsHandle,
						columnHandle ) );
			}
		}
	}

	private void updateNewDefnMap( Object oldName, Object newName )
	{
		if ( rsColumnMap != null
				&& oldName != null && !oldName.equals( newName )
				&& rsColumnMap.get( oldName ) != null )
		{
			rsColumnMap.put( newName, rsColumnMap.get( oldName ) );
			rsColumnMap.remove( oldName );
			columnHintMap.put( newName, columnHintMap.get( oldName ) );
			columnHintMap.remove( oldName );
		}
	}

	private final int getTypeIndex( String dataTypeName )
	{
		for ( int n = 0; n < dataTypes.length; n++ )
		{
			if ( dataTypes[n].equals( dataTypeName ) )
			{
				return n;
			}
		}

		return -1;
	}

	private final String getTypeString( int index )
	{
		if ( index > -1 && index < dataTypes.length )
		{
			return dataTypes[index];
		}

		return null;
	}

	private final String getTypeDisplayName( String typeName )
	{
		for ( int n = 0; n < dataTypes.length; n++ )
		{
			if ( dataTypes[n].equals( typeName ) )
			{
				return dataTypeDisplayNames[n];
			}
		}

		return dataTypeDisplayNames[0];
	}

	/**
	 * Depending on the value of the parameters the properties of various controls
	 * on this page are set
	 */
	private void setPageProperties() 
	{		
		boolean columnsExist = false;
		
		columnsExist = columnHandlesList != null && columnHandlesList.size()>0 ;
		viewer.getDownButton().setEnabled(columnsExist);
		viewer.getUpButton().setEnabled(columnsExist);
		viewer.getRemoveButton().setEnabled(columnsExist);
		viewer.getRemoveMenuItem( ).setEnabled( columnsExist );
		viewer.getRemoveAllMenuItem( ).setEnabled( columnsExist );		
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.birt.report.designer.ui.dialogs.properties.IPropertyPage#pageActivated()
	 */
	public void pageActivated( )
	{
		getContainer( ).setMessage( DEFAULT_MESSAGE, IMessageProvider.NONE );
		if ( modelChanged )
		{
			modelChanged = false;
			refreshColumns( );
		}
	}

	/**
	 * Refreshes the column list adds any new column retrieved. This
	 * method doesn't clear unused column. It is the users responsibility to
	 * delete individual column through the UI.
	 */
	private void refreshColumns( )
	{
		try
		{
			//Bugzilla#104185
			// It's caused by conflict position data. Position from model is
			// staring with 0 in initialization, but from dte is starting
			// with 1. In global perspective, starting with 1 is wider used, and
			// refreshPosition will reset position starting with 1 to model after page activated
//			DataSetItemModel[] items = ( (DataSetEditorDialog) this.getContainer( ) ).getCurrentItemModel( false,
//					true );
//			if ( items != null )
//			{
//				for ( int i = 0; i < items.length; i++ )
//				{
//					DataSetItemModel dsItem = items[i];
//
//					ColumnDefn defn = null;
//					if ( dsItem.getPosition( ) > 0 )
//					{
//						defn = findColumnByPosition( dsItem.getPosition( ) );
//					}
//					else
//					{
//						defn = findColumnByName( dsItem.getName( ) );
//					}
//					if ( defn == null )
//					{
//						defn = new ColumnDefn( );
//						defn.setDataType( dsItem.getDataTypeName( ) );
//						defn.setDisplayName( dsItem.getDisplayName( ) );
//						defn.setHelpText( dsItem.getHelpText( ) );
//						defn.setAlias( dsItem.getAlias( ) );
//						defn.setColumnName( dsItem.getName( ) );
//
//						if ( defn.getColumnName( ) == null )
//						{
//							defn.setColumnName( getUniqueName( ) );
//						}
//						addNewDefn( defn );
//					}
//				}
//			}
			refreshPositions( );
			viewer.getViewer( ).refresh( );
		}
		catch ( Exception e )
		{
			ExceptionHandler.handle( e );
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.birt.report.model.core.Listener#elementChanged(org.eclipse.birt.report.model.api.DesignElementHandle,
	 *      org.eclipse.birt.report.model.activity.NotificationEvent)
	 */
	public void elementChanged( DesignElementHandle focus, NotificationEvent ev )
	{
		modelChanged = true;
	}

	protected final ColumnDefn findColumnByPosition( int position )
	{
		ResultSetColumnHandle column = null;
		ColumnHintHandle hint = null;
		//find ResultSetColumnHandle
		if ( rsColumns != null )
		{
			Iterator iter = rsColumns.iterator( );
			if ( iter != null )
			{
				while ( iter.hasNext( ) )
				{
					column = (ResultSetColumnHandle) iter.next( );
					if ( column.getPosition( ) != null
							&& column.getPosition( ).intValue( ) == position )
					{
						break;
					}
				}
			}
		}
		//find ColumnHintHandle
		if ( this.columnHints != null )
		{
			Iterator iter = columnHints.iterator( );
			if ( iter != null )
			{
				while ( iter.hasNext( ) )
				{
					hint = (ColumnHintHandle) iter.next( );
					if ( hint.getColumnName() != null
							&& hint.getColumnName().equals(column.getColumnName()) )
					{
						break;
					}
				}
			}
		}
		
		if( column != null )
			return new ColumnDefn( new ColumnHandles(column, hint));
		return null;
	}

	protected final ColumnDefn findColumnByName( String name )
	{
		if ( columnHints != null )
		{
			return new ColumnDefn( new ColumnHandles((ResultSetColumnHandle) rsColumnMap.get(name),(ColumnHintHandle) columnHintMap.get( name )) );
		}
		return null;
	}

	/**
	 * Re-indexes the parameters starting at 1 from the 1st in the list.
	 */
	protected final void refreshPositions( )
	{
		if ( rsColumns == null )
			return;

		int position = 1;
		Iterator iter = rsColumns.iterator( );
		if ( iter != null )
		{
			while ( iter.hasNext( ) )
			{
				ResultSetColumnHandle column = (ResultSetColumnHandle) iter.next( );
				column.setPosition( new Integer( position++ ) );
			}
		}
	}

	protected final String getUniqueName( )
	{
		int n = 1;
		String prefix = "column"; //$NON-NLS-1$
		StringBuffer buf = new StringBuffer( );
		while ( buf.length( ) == 0 )
		{
			buf.append( prefix ).append( n++ );
			if ( rsColumns == null )
				continue;

			Iterator iter = rsColumns.iterator( );
			if ( iter != null )
			{
				while ( iter.hasNext( ) && buf.length( ) > 0 )
				{
					ResultSetColumnHandle column = (ResultSetColumnHandle) iter.next( );
					if ( buf.toString( )
							.equalsIgnoreCase( column.getColumnName( ) ) )
					{
						buf.setLength( 0 );
					}
				}
			}
		}
		return buf.toString( );
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.birt.report.designer.ui.dialogs.properties.AbstractPropertyPage#performCancel()
	 */
	public boolean performCancel( )
	{
		disposeAll( );
		return super.performCancel( );
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.birt.report.designer.ui.dialogs.properties.AbstractPropertyPage#performOk()
	 */
	public boolean performOk( )
	{
		if ( isValid( ) )
		{
			refreshPositions( );
			disposeAll( );
			return super.performOk( );
		}
		else
			return false;
	}

	/**
	 * Check the alias names whether is valid. The invalid situation may be the
	 * same name of alias or the same name between column name and alias name.
	 *  
	 */
	private boolean isValid( )
	{
		boolean validate = true;
		String newColumnNameOrAlias;

		if ( columnHints == null )
		{
			columnHints = ( (DataSetHandle) getContainer( ).getModel( ) ).getPropertyHandle( DataSetHandle.COLUMN_HINTS_PROP );
		}

		Iterator iterator1 = columnHints.iterator( );
		for ( int i = 0; iterator1.hasNext( ) && validate; i++ )
		{
			ColumnHintHandle columnHint = (ColumnHintHandle) iterator1.next( );

			newColumnNameOrAlias = columnHint.getAlias( );
			Iterator iterator2 = columnHints.iterator( );
			if ( newColumnNameOrAlias != null
					&& newColumnNameOrAlias.length( ) > 0 )
			{
				for ( int n = 0; iterator2.hasNext( ); n++ )
				{
					ColumnHintHandle columnHint2 = (ColumnHintHandle) iterator2.next( );
					if ( i == n )
						continue;

					if ( ( columnHint2.getColumnName( ) != null && columnHint2.getColumnName( )
							.equals( newColumnNameOrAlias ) )
							|| ( columnHint2.getAlias( ) != null && columnHint2.getAlias( )
									.equals( newColumnNameOrAlias ) ) )
					{
						validate = false;
						getContainer( ).setMessage( Messages.getFormattedString( "dataset.editor.error.columnOrAliasNameAlreadyUsed",
								new Object[]{
										newColumnNameOrAlias,
										n>i?new Integer( i + 1 ):new Integer( n + 1 ),
										n>i?new Integer( n + 1 ):new Integer( i + 1 )
								} ),
								IMessageProvider.ERROR );
						break;
					}
				}
			}
		}
		return validate;
	}
	
	private void updateMessage( )
	{
		if ( isValid( ) )
			getContainer( ).setMessage( Messages.getString( "dataset.editor.outputColumns" ), //$NON-NLS-1$
					IMessageProvider.NONE );
	}
	
	private void disposeAll( )
	{
		rsColumnMap = null;
		columnHintMap = null;
	//	selectorImage.dispose( );
		( (DataSetHandle) getContainer( ).getModel( ) ).removeListener( this );
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.birt.report.designer.ui.dialogs.properties.AbstractDescriptionPropertyPage#getPageDescription()
	 */
	public String getPageDescription( )
	{
		return Messages.getString( "OutputColumnDefnPage.description" ); //$NON-NLS-1$
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.birt.report.designer.ui.dialogs.properties.AbstractPropertyPage#canLeave()
	 */
	public boolean canLeave( )
	{
		if ( isValid( ) )
		{
			refreshPositions( );
			return super.canLeave( );
		}
		else
			return false;
	}
	
	private void refreshCachedMap( )
	{
		columnHintMap.clear( );
		rsColumnMap.clear( );

		for ( Iterator iterator = columnHints.iterator( ); iterator.hasNext( ); )
		{
			ColumnHintHandle handle = (ColumnHintHandle) iterator.next( );
			columnHintMap.put( handle.getColumnName( ), handle.getStructure( ) );
		}

		IStructure toDelete = null;
		if ( rsColumns == null )
			return;

		for ( Iterator iterator = rsColumns.iterator( ); iterator.hasNext( ); )
		{
			ResultSetColumnHandle handle = (ResultSetColumnHandle) iterator.next( );
			if ( columnHintMap.get( handle.getColumnName( ) ) == null )
			{
				toDelete = handle.getStructure( );
				continue;
			}
			rsColumnMap.put( handle.getColumnName( ), handle.getStructure( ) );
		}

		if ( toDelete != null )
		{
			try
			{
				rsColumns.removeItem( toDelete );
			}
			catch ( PropertyValueException e )
			{
				ExceptionHandler.handle( e );
			}
		}
		
	}

	/**
	 * 
	 * 
	 */
	private void createCachedMap( )
	{
		if ( rsColumns == null )
			return;
		
		for ( Iterator iterator = rsColumns.iterator( ); iterator.hasNext( ); )
		{
			ResultSetColumnHandle handle = (ResultSetColumnHandle) iterator.next( );
			rsColumnMap.put( handle.getColumnName( ), handle.getStructure( ) );
		}
		for ( Iterator iterator = columnHints.iterator( ); iterator.hasNext( ); )
		{
			ColumnHintHandle handle = (ColumnHintHandle) iterator.next( );
			columnHintMap.put( handle.getColumnName( ), handle.getStructure( ) );
		}
	}
	
	/**
	 * init the column handle list
	 *
	 */
	private void initColumnHandles( )
	{
		if ( columnHints == null || rsColumns == null )
			return;

		Iterator it = rsColumns.iterator( );
		Iterator it1 = columnHints.iterator( );
		while ( it.hasNext( ) )
		{
			columnHandlesList.add( new ColumnHandles( (ResultSetColumnHandle) it.next( ),
					(ColumnHintHandle) it1.next( ) ) );
		}
	}
	/**
	 * A class that contain one ResultSetColumnHandle and one ColumnHintHandle.
	 * @author lzhu
	 *
	 */
	private class ColumnHandles
	{
		private ResultSetColumnHandle rsColumnHandle;
		private ColumnHintHandle chHandle;
		ColumnHandles( ResultSetColumnHandle rsch, ColumnHintHandle chh)
		{
			this.rsColumnHandle = rsch;
			this.chHandle = chh;
		}
		public ResultSetColumnHandle getResultSetColumnHandle()
		{
			return this.rsColumnHandle;
		}
		public ColumnHintHandle getColumnHintHandle()
		{
			return this.chHandle;
		}
	}
	/**
	 * The class which serves as input data of one single table item in column definition table.
	 * @author lzhu
	 *
	 */
	private class ColumnDefn
	{

		private ResultSetColumnHandle rsColumnHandle;
		private ColumnHintHandle columnHintHandle;
		
		private ResultSetColumn rsColumn;
		private ColumnHint columnHint;
		
		public ColumnDefn(){
			rsColumn = new ResultSetColumn();
			columnHint = new ColumnHint();
		}
		
		public ColumnDefn( ColumnHandles hc )
		{
			this.rsColumnHandle = hc.getResultSetColumnHandle();
			this.columnHintHandle = hc.getColumnHintHandle();
		}

	
		public ResultSetColumn getResultSetColumn( )
		{
			if(this.rsColumnHandle!=null)
				return (ResultSetColumn)this.rsColumnHandle.getStructure();
			else
				return this.rsColumn;
		}

		public ColumnHint getColumnHint( )
		{
			if (this.columnHintHandle!=null)
				return (ColumnHint)this.columnHintHandle.getStructure();
			else
				return this.columnHint;
		}

		public String getColumnName( )
		{
			if ( this.rsColumnHandle!= null)
				return this.rsColumnHandle.getColumnName();
			else if(this.rsColumn != null)
				return this.rsColumn.getColumnName();
			return null;
		}

		/**
		 * @param columnName
		 *            The columnName to set.
		 */
		public void setColumnName( String columnName )
		{
			if ( this.rsColumnHandle != null && this.columnHintHandle != null )
			{
				try
				{
					rsColumnHandle.setColumnName( columnName );
					columnHintHandle.setColumnName( columnName );
				}
				catch ( SemanticException e )
				{
				}
			}
			else if ( this.rsColumn != null && this.columnHint != null )
			{
				rsColumn.setColumnName( columnName );
				columnHint.setProperty( ColumnHint.COLUMN_NAME_MEMBER,
						columnName );
			}
		}

		/**
		 * @return Returns the alias.
		 */
		public String getAlias( )
		{
			if ( this.columnHintHandle!= null)
				return columnHintHandle.getAlias();
			else 
				return (String)columnHint.getProperty(null,ColumnHint.ALIAS_MEMBER);
		}

		/**
		 * @param alias
		 *            The alias to set.
		 */
		public void setAlias( String alias )
		{
			if(this.columnHintHandle!=null)
				columnHintHandle.setAlias( alias );
			else
				columnHint.setProperty(ColumnHint.ALIAS_MEMBER, alias );
		}

		/**
		 * @return Returns the dataType.
		 */
		public String getDataType( )
		{
			if(this.rsColumnHandle!=null)
				return this.rsColumnHandle.getDataType();
			else 
				return this.rsColumn.getDataType();
		}

		/**
		 * @param dataType
		 *            The dataType to set.
		 */
		public void setDataType( String dataType )
		{
			try
			{
				if(rsColumnHandle!=null)
					rsColumnHandle.setDataType( dataType );
				else
					rsColumn.setDataType( dataType );
			}
			catch ( SemanticException e )
			{
				e.printStackTrace();
			}
		}

		/**
		 * @return Returns the displayName.
		 */
		public String getDisplayName( )
		{
			if(this.columnHintHandle!=null)
				return this.columnHintHandle.getDisplayName();
			else
				return (String) columnHint.getProperty( null,
						ColumnHint.DISPLAY_NAME_MEMBER );
		}

		/**
		 * @param displayName
		 *            The displayName to set.
		 */
		public void setDisplayName( String displayName )
		{
			if(this.columnHintHandle!=null)
				columnHintHandle.setDisplayName(displayName);
			else
				columnHint.setProperty( ColumnHint.DISPLAY_NAME_MEMBER, displayName );
		}

		/**
		 * @return Returns the helpText.
		 */
		public String getHelpText( )
		{
			if(this.columnHintHandle!=null)
				return columnHintHandle.getHelpText();
			else
				return (String) columnHint.getProperty( null,
						ColumnHint.HELP_TEXT_MEMBER );
		}

		/**
		 * @param helpText
		 *            The helpText to set.
		 */
		public void setHelpText( String helpText )
		{
			if(this.columnHintHandle!=null)
				columnHintHandle.setHelpText(helpText);
			else
				columnHint.setProperty( ColumnHint.HELP_TEXT_MEMBER, helpText );
		}

		public void setProperty( Object property, Object value )
		{
			if ( property.equals( NAME ) )
			{
				setColumnName( (String) value );
			}
			else if ( property.equals( TYPE ) )
			{
				setDataType( (String) value );
			}
			else if ( property.equals( ALIAS ) )
			{
				setAlias( (String) value );
			}
			else if ( property.equals( DISPLAY_NAME ) )
			{
				setDisplayName( (String) value );
			}
			else if ( property.equals( HELP_TEXT ) )
			{
				setHelpText( (String) value );
			}
		}

		public Object getProperty( Object property )
		{
			if ( property.equals( NAME ) )
			{
				return getColumnName( );
			}
			else if ( property.equals( TYPE ) )
			{
				return getDataType( );
			}
			else if ( property.equals( ALIAS ) )
			{
				return getAlias( );
			}
			else if ( property.equals( DISPLAY_NAME ) )
			{
				return getDisplayName( );
			}
			else if ( property.equals( HELP_TEXT ) )
			{
				return getHelpText( );
			}
			return null;
		}
	}
	
	private class OutputColumnTableViewer
	{
	    private TableViewer viewer;
	    private Composite mainControl;
	    private Button btnRemove;
	    private Button btnUp;
	    private Button btnDown;
	    private MenuItem itmRemove;
	    private MenuItem itmRemoveAll;
	    private Menu menu;

	    public OutputColumnTableViewer(Composite parent, boolean showMenus, boolean showButtons, boolean enableKeyStrokes)
	    {
	        mainControl = new Composite(parent, SWT.NONE);
	        GridLayout layout = new GridLayout();
	        layout.numColumns = 2;
	        mainControl.setLayout(layout);

	        GridData data = null;
	        viewer = new TableViewer(mainControl, SWT.FULL_SELECTION);
	        data = new GridData(GridData.FILL_BOTH);
	        viewer.getControl().setLayoutData(data);
	        
	        viewer.getTable( ).setHeaderVisible( true );
	        viewer.getTable( ).setLinesVisible( true );

	        if(showButtons)
	        {
	            Composite btnComposite = new Composite(mainControl, SWT.NONE);
	            data = new GridData();
	            data.verticalAlignment = SWT.CENTER;
	            btnComposite.setLayoutData(data);
	            GridLayout btnLayout = new GridLayout();
	            layout.verticalSpacing = 20;
	            btnComposite.setLayout(btnLayout);
	            
	            GridData btnData = new GridData(GridData.CENTER);
	            btnData.widthHint = 20;
	            btnData.heightHint = 20;
	            btnUp = new Button(btnComposite, SWT.ARROW | SWT.UP);
	            btnUp.setLayoutData(btnData);
	            btnUp.addSelectionListener(new SelectionListener(){

	                public void widgetSelected(SelectionEvent e)
	                {
	                    //Get the current selection and delete that row
	                    int index = viewer.getTable( ).getSelectionIndex( );
	                    if ( index - 1 >= 0
								&& index < columnHandlesList.size( ) )
						{
	                    	ColumnHandles columnHandle1 = (ColumnHandles)columnHandlesList.get(index);
	                    	viewer.cancelEditing();
	                        try
							{
								columnHandlesList.set( index,
										columnHandlesList.get( index - 1 ) );
								columnHandlesList.set( index - 1, columnHandle1 );
								if ( rsColumns != null )
									rsColumns.moveItem( index, index - 1 );
							}
	                        catch ( Exception e1 )
	                        {
	                            ExceptionHandler.handle( e1 );
	                        }
	                        viewer.refresh( );
	                        viewer.getTable( ).select( index - 1);
	                    }

	                }

	                public void widgetDefaultSelected(SelectionEvent e)
	                {
	                }
	                
	            });
	            
	            btnData = new GridData(GridData.CENTER);
	            btnData.widthHint = 20;
	            btnData.heightHint = 20;
	            btnRemove = new Button(btnComposite, SWT.PUSH);
	            btnRemove.setImage(PlatformUI.getWorkbench( ).getSharedImages( ).getImage( ISharedImages.IMG_TOOL_DELETE ));
	            btnRemove.setLayoutData(btnData);
	            btnRemove.addSelectionListener(new SelectionListener(){

	                public void widgetSelected(SelectionEvent e)
	                {
	                    removeSelectedItem();
	                }

	                public void widgetDefaultSelected(SelectionEvent e)
	                {
	                }
	                
	            });
	            
	            btnData = new GridData(GridData.CENTER);
	            btnData.widthHint = 20;
	            btnData.heightHint = 20;
	            btnDown = new Button(btnComposite, SWT.ARROW | SWT.DOWN);
	            btnDown.setLayoutData(btnData);
	            btnDown.addSelectionListener(new SelectionListener(){

	                public void widgetSelected(SelectionEvent e)
	                {
	                    //Get the current selection and delete that row
	                    int index = viewer.getTable( ).getSelectionIndex( );
	                    
	                    if ( index > -1
	                            && index < columnHandlesList.size( ) - 1)
	                    {
	                        viewer.cancelEditing();
	                        try
	                        {
	                        	ColumnHandles columnHandle = (ColumnHandles)columnHandlesList.get(index);
	                        	columnHandlesList.set( index,columnHandlesList.get( index + 1 ) );
	                        	columnHandlesList.set( index + 1, columnHandle );
	                        	if ( rsColumns != null )
									rsColumns.moveItem( index, index + 1 );
							}
	                        catch ( Exception e1 )
	                        {
	                            ExceptionHandler.handle( e1 );
	                        }
	                        viewer.refresh( );
	                        viewer.getTable( ).select( index + 1);
	                    }
	                }

	                public void widgetDefaultSelected(SelectionEvent e)
	                {
	                }
	                
	            });
	        }
	        
	        if(showMenus)
	        {
	            menu = new Menu(viewer.getTable());
	            menu.addMenuListener(new MenuAdapter(){
	                public void menuShown(MenuEvent e)
	                {
	                    viewer.cancelEditing();
	                }
	            });
	            itmRemove = new MenuItem(menu, SWT.NONE);
	            itmRemove .setText(Messages.getString("PropertyHandleTableViewer.Menu.Remove")); //$NON-NLS-1$
	            itmRemove .addSelectionListener(new SelectionAdapter(){

	                public void widgetSelected(SelectionEvent e)
	                {
	                    removeSelectedItem();
	                }

	            });
	            itmRemoveAll = new MenuItem(menu, SWT.NONE);
	            itmRemoveAll.setText(Messages.getString("PropertyHandleTableViewer.Menu.RemoveAll")); //$NON-NLS-1$
	            itmRemoveAll.addSelectionListener( new SelectionAdapter( ) {

					public void widgetSelected( SelectionEvent e )
					{
						try
						{
							for ( int index = 0; index < columnHandlesList.size( ); )
							{
								columnHandlesList.remove( 0 );
							}
							if ( rsColumns != null )
								rsColumns.clearValue( );
							columnHints.clearValue( );
							viewer.refresh( );
						}
						catch ( Exception e1 )
						{
							// TODO Auto-generated catch block
							e1.printStackTrace( );
						}
						columnHintMap.clear( );
						rsColumnMap.clear( );
						setPageProperties( );
					}
				} );

	            viewer.getTable().setMenu(menu);
	        }
	        
	        if(enableKeyStrokes)
	        {
	            viewer.getTable().addKeyListener(new KeyListener(){

	                public void keyPressed(KeyEvent e)
	                {
	                }

	                public void keyReleased(KeyEvent e)
	                {
	                    if ( e.keyCode == SWT.DEL )
	                    {
	                        removeSelectedItem();
	                    }
	                }
	                
	            });
	        }
	    }
	    
	    public TableViewer getViewer()
	    {
	        return viewer;
	    }
	    
	    public Composite getControl()
	    {
	        return mainControl;
	    }
	    
	    public Button getUpButton()
	    {
	        return btnUp;
	    }
	    
	    public Menu getMenu()
	    {
	        return menu;
	    }
	    
	    public Button getDownButton()
	    {
	        return btnDown;
	    }
	    
	    public Button getRemoveButton()
	    {
	        return btnRemove;
	    }
	    
	    public MenuItem getRemoveMenuItem()
	    {
	        return itmRemove;
	    }
	    
	    public MenuItem getRemoveAllMenuItem()
	    {
	        return itmRemoveAll;
	    }
	    
	    private final void removeSelectedItem()
	    {
	        int index = viewer.getTable( ).getSelectionIndex( );
			int count = columnHandlesList.size( ); 
	        //Do not allow deletion of the last item.
	        if ( index > -1 && index < count )
	        {
	            try
	            {
					columnHandlesList.remove( index );
					if ( rsColumns != null )
						rsColumns.removeItem( index );
					columnHints.removeItem( index );
				}
	            catch ( Exception e1 )
	            {
	                ExceptionHandler.handle( e1 );
	            }
	            viewer.refresh( );
	            viewer.getTable( ).select( index );
	            refreshCachedMap( );
	        }
	        setPageProperties( );
	    }
	}

	/* (non-Javadoc)
	 * @see org.eclipse.birt.report.designer.ui.dialogs.properties.IPropertyPage#getToolTip()
	 */
	public String getToolTip( )
	{
		return Messages.getString("OutputColumnDefnPage.OutputColumns.Tooltip"); //$NON-NLS-1$
	}
}