/*******************************************************************************
 * Copyright (c) 2000, 2006 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.jface.viewers;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

import org.eclipse.jface.util.Assert;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.TableEditor;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Widget;

/**
 * A concrete viewer based on a SWT <code>Table</code> control.
 * <p>
 * This class is not intended to be subclassed outside the viewer framework. It
 * is designed to be instantiated with a pre-existing SWT table control and
 * configured with a domain-specific content provider, table label provider,
 * element filter (optional), and element sorter (optional).
 * </p>
 * <p>
 * Label providers for table viewers must implement either the
 * <code>ITableLabelProvider</code> or the <code>ILabelProvider</code>
 * interface (see <code>TableViewer.setLabelProvider</code> for more details).
 * </p>
 * <p>
 * As of 3.1 the TableViewer now supports the SWT.VIRTUAL flag. If the
 * underlying table is SWT.VIRTUAL, the content provider may implement
 * {@link ILazyContentProvider} instead of {@link IStructuredContentProvider}.
 * Note that in this case, the viewer does not support sorting or filtering.
 * Also note that in this case, the Widget based APIs may return null if the
 * element is not specified or not created yet.
 * </p>
 * <p>
 * Users of SWT.VIRTUAL should also avoid using getItems() from the Table within
 * the TreeViewer as this does not necessarily generate a callback for the
 * TreeViewer to populate the items. It also has the side effect of creating all
 * of the items thereby eliminating the performance improvements of SWT.VIRTUAL.
 * </p>
 * 
 * @see SWT#VIRTUAL
 * @see #doFindItem(Object)
 * @see #internalRefresh(Object, boolean)
 */
public class TableViewer extends StructuredViewer {
		
	private class VirtualManager{

		/**
		 * The currently invisible elements as provided 
		 * by the content provider or by addition.
		 * This will not be populated by an ILazyStructuredContentProvider
		 * as an ILazyStructuredContentProvider is only queried
		 * on the virtual callabck.
		 */
		private Object[] cachedElements = new Object[0];
		/**
		 * Create a new instance of the receiver.
		 *
		 */
		public VirtualManager(){
			addTableListener();
		}

		
		/**
		 * Add the listener for SetData on the table
		 */
		private void addTableListener() {
			table.addListener(SWT.SetData,new Listener(){
				/* (non-Javadoc)
				 * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
				 */
				public void handleEvent(Event event) {
					TableItem item = (TableItem) event.item;
					final int index = table.indexOf(item);
					Object element = resolveElement(index);
					if(element == null){
						//Didn't find it so make a request
						//Keep looking if it is not in the cache.
						IContentProvider contentProvider = getContentProvider();
						//If we are building lazily then request lookup now
						if(contentProvider instanceof ILazyContentProvider){
							((ILazyContentProvider) contentProvider).
								updateElement(index);
							return;
						}	
					}
						
					
					associate(element,item);
					updateItem(item,element);
				}

			});
		}
		
		/**
		 * Get the element at index.Resolve it lazily if this
		 * is available.
		 * @param index
		 * @return Object or <code>null</code> if it could
		 * not be found
		 */
		protected Object resolveElement(int index) {
			
			Object element = null;
			if(index < cachedElements.length) {
				element =  cachedElements[index];
			}
			
			return element;
		}

		/**
		 * A non visible item has been added.
		 * @param element
		 * @param index
		 */
		public void notVisibleAdded(Object element, int index) {
			
			int requiredCount = index + 1;
			
			if(requiredCount > getTable().getItemCount()){
				getTable().setItemCount(requiredCount);
				Object[] newCache = new Object[requiredCount];
				System.arraycopy(cachedElements, 0, newCache, 0, cachedElements.length);
				cachedElements = newCache;
			}
			
			
			cachedElements[index] = element;
			
		}
		
	}
	
	private VirtualManager virtualManager;
	
	/**
	 * TableColorAndFontNoOp is an optimization for tables without
	 * color and font support.
	 * @see ITableColorProvider
	 * @see ITableFontProvider
	 */
	private class TableColorAndFontNoOp{
		
		/**
		 * Create a new instance of the receiver.
		 *
		 */
		TableColorAndFontNoOp(){
			
		}
		
		/**
		 * Set the fonts and colors for the tableItem if there is a color
		 * and font provider available.
		 * @param tableItem The item to update.
		 * @param element The element being represented
		 * @param column The column index
		 */
		public void setFontsAndColors(TableItem tableItem, Object element, int column){
		}	
		
	}

	/**
	 * TableColorAndFontCollector is an helper class for color and font
	 * support for tables that support the ITableFontProvider and
	 * the ITableColorProvider.
	 * @see ITableColorProvider
	 * @see ITableFontProvider
	 */
	
	private class TableColorAndFontCollector extends TableColorAndFontNoOp{
		
		ITableFontProvider fontProvider = null;
		ITableColorProvider colorProvider = null;
		
		/**
		 * Create an instance of the receiver. Set the color and font
		 * providers if provider can be cast to the correct type.
		 * @param provider IBaseLabelProvider
		 */
		public TableColorAndFontCollector(IBaseLabelProvider provider){
			if(provider instanceof ITableFontProvider) {
				fontProvider = (ITableFontProvider) provider;
			}
			if(provider instanceof ITableColorProvider) {
				colorProvider = (ITableColorProvider) provider;
			}
		}
	
		
		/**
		 * Set the fonts and colors for the tableItem if there is a color
		 * and font provider available.
		 * @param tableItem The item to update.
		 * @param element The element being represented
		 * @param column The column index
		 */
		public void setFontsAndColors(TableItem tableItem, Object element, int column){
			if (colorProvider != null) {
				tableItem.setBackground(column, colorProvider.getBackground(element,
						column));
				tableItem.setForeground(column, colorProvider.getForeground(element,
						column));
			}
			if(fontProvider != null) {
				tableItem.setFont(column,fontProvider.getFont(element,column));
			}
		}	
		
	}
	
	/**
	 * Internal table viewer implementation.
	 */
	private TableEditorImpl tableViewerImpl;

	/**
	 * This viewer's table control.
	 */
	private Table table;

	/**
	 * This viewer's table editor.
	 */
	private TableEditor tableEditor;
	
	/**
	 * The color and font collector for the cells.
	 */
	private TableColorAndFontNoOp tableColorAndFont = new TableColorAndFontNoOp();
	
	/**
	 * Creates a table viewer on a newly-created table control under the given
	 * parent. The table control is created using the SWT style bits
	 * <code>MULTI, H_SCROLL, V_SCROLL,</code> and <code>BORDER</code>. The
	 * viewer has no input, no content provider, a default label provider, no
	 * sorter, and no filters. The table has no columns.
	 * 
	 * @param parent
	 *            the parent control
	 */
	public TableViewer(Composite parent) {
		this(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
	}

	/**
	 * Creates a table viewer on a newly-created table control under the given
	 * parent. The table control is created using the given style bits. The
	 * viewer has no input, no content provider, a default label provider, no
	 * sorter, and no filters. The table has no columns.
	 * 
	 * @param parent
	 *            the parent control
	 * @param style
	 *            SWT style bits
	 */
	public TableViewer(Composite parent, int style) {
		this(new Table(parent, style));			
	}

	/**
	 * Creates a table viewer on the given table control. The viewer has no
	 * input, no content provider, a default label provider, no sorter, and no
	 * filters.
	 * 
	 * @param table
	 *            the table control
	 */
	public TableViewer(Table table) {
		this.table = table;
		hookControl(table);
		tableEditor = new TableEditor(table);
		initTableViewerImpl();
		initializeVirtualManager(table.getStyle());
	}
	
	/**
	 * Initialize the virtual manager to manage the virtual state
	 * if the table is VIRTUAL. If not use the default no-op
	 * version.
	 * @param style
	 */
	private void initializeVirtualManager(int style) {
		if((style & SWT.VIRTUAL) == 0) {
			return;
		}
			
		virtualManager = new VirtualManager();	
	}

	/**
	 * Adds the given elements to this table viewer. If this viewer does not
	 * have a sorter, the elements are added at the end in the order given;
	 * otherwise the elements are inserted at appropriate positions.
	 * <p>
	 * This method should be called (by the content provider) when elements have
	 * been added to the model, in order to cause the viewer to accurately
	 * reflect the model. This method only affects the viewer, not the model.
	 * </p>
	 * 
	 * @param elements
	 *            the elements to add
	 */
	public void add(Object[] elements) {
		assertElementsNotNull(elements);
		Object[] filtered = filter(elements);
		
		for (int i = 0; i < filtered.length; i++) {
			Object element = filtered[i];
			int index = indexForElement(element);
			createItem(element, index);
		}
	}

	/**
	 * Create a new TableItem at index if required.
	 * @param element
	 * @param index
	 * 
	 * @since 3.1
	 */
	private void createItem(Object element, int index) {
		if(virtualManager == null) {
			updateItem(new TableItem(getTable(), SWT.NONE, index), element);
		} else{
			virtualManager.notVisibleAdded(element,index);
			
		}
	}

	/**
	 * Adds the given element to this table viewer. If this viewer does not have
	 * a sorter, the element is added at the end; otherwise the element is
	 * inserted at the appropriate position.
	 * <p>
	 * This method should be called (by the content provider) when a single
	 * element has been added to the model, in order to cause the viewer to
	 * accurately reflect the model. This method only affects the viewer, not
	 * the model. Note that there is another method for efficiently processing
	 * the simultaneous addition of multiple elements.
	 * </p>
	 * 
	 * @param element
	 *            the element to add
	 */
	public void add(Object element) {
		add(new Object[] { element });
	}

	/**
	 * Cancels a currently active cell editor. All changes already done in the
	 * cell editor are lost.
	 */
	public void cancelEditing() {
		tableViewerImpl.cancelEditing();
	}

	/*
	 *  (non-Javadoc)
	 * @see org.eclipse.jface.viewers.StructuredViewer#doFindInputItem(java.lang.Object)
	 */
	protected Widget doFindInputItem(Object element) {
		if (equals(element, getRoot())) {
			return getTable();
		}
		return null;
	}

	/*
	 *  (non-Javadoc)
	 * @see org.eclipse.jface.viewers.StructuredViewer#doFindItem(java.lang.Object)
	 */
	protected Widget doFindItem(Object element) {
		
		TableItem[] children = table.getItems();
		for (int i = 0; i < children.length; i++) {
			TableItem item = children[i];
			Object data = item.getData();
			if (data != null && equals(data, element)) {
				return item;
			}
		}

		return null;
	}

	/*
	 *  (non-Javadoc)
	 * @see org.eclipse.jface.viewers.StructuredViewer#doUpdateItem(org.eclipse.swt.widgets.Widget, java.lang.Object, boolean)
	 */
	protected void doUpdateItem(Widget widget, Object element, boolean fullMap) {
		if (widget instanceof TableItem) {
			final TableItem item = (TableItem) widget;

			// remember element we are showing
			if (fullMap) {
				associate(element, item);
			} else {
            	Object data = item.getData();
            	if (data != null) {
            		unmapElement(data, item);
            	}
				item.setData(element);
				mapElement(element, item);
			}

			IBaseLabelProvider prov = getLabelProvider();
			ITableLabelProvider tprov = null;		
			ILabelProvider lprov = null;
			IViewerLabelProvider vprov = null;
			
			if(prov instanceof ILabelProvider) {
				lprov = (ILabelProvider) prov;
			}
			
			if (prov instanceof IViewerLabelProvider) {
				vprov = (IViewerLabelProvider) prov;
			} 

			if (prov instanceof ITableLabelProvider) {
				tprov = (ITableLabelProvider) prov;
			} 
			
			int columnCount = table.getColumnCount();
			TableItem ti = item;
			getColorAndFontCollector().setFontsAndColors(element);
			
			// Also enter loop if no columns added. See 1G9WWGZ: JFUIF:WINNT -
			// TableViewer with 0 columns does not work
			for (int column = 0; column < columnCount || column == 0; column++) {
				// Similar code in TreeViewer.doUpdateItem()
				String text = "";//$NON-NLS-1$
				Image image = null;
				tableColorAndFont.setFontsAndColors(ti,element,column);

				if (tprov == null) {
					if (column == 0) {
						ViewerLabel updateLabel = new ViewerLabel(item
								.getText(), item.getImage());
						
						if(vprov != null) {
							buildLabel(updateLabel,element,vprov);
						} else{
							if(lprov != null) {
								buildLabel(updateLabel,element,lprov);
							}
						}
						
//						As it is possible for user code to run the event 
			            //loop check here.
						if (item.isDisposed()) {
			                unmapElement(element, item);
			                return;
			            }   
						
						text = updateLabel.getText();
						image = updateLabel.getImage();
					}
				} else {
					text = tprov.getColumnText(element, column);
					image = tprov.getColumnImage(element, column);
				}

				//Avoid setting text to null
				if (text == null) {
					text = ""; //$NON-NLS-1$
				}
				ti.setText(column, text);
				if (ti.getImage(column) != image) {
					ti.setImage(column, image);
				}
			}
			
			
			getColorAndFontCollector().applyFontsAndColors(ti);
		}
	}

	/**
	 * Starts editing the given element.
	 * 
	 * @param element
	 *            the element
	 * @param column
	 *            the column number
	 */
	public void editElement(Object element, int column) {
		tableViewerImpl.editElement(element, column);
	}

	/**
	 * Returns the cell editors of this table viewer.
	 * 
	 * @return the list of cell editors
	 */
	public CellEditor[] getCellEditors() {
		return tableViewerImpl.getCellEditors();
	}

	/**
	 * Returns the cell modifier of this table viewer.
	 * 
	 * @return the cell modifier
	 */
	public ICellModifier getCellModifier() {
		return tableViewerImpl.getCellModifier();
	}

	/**
	 * Returns the column properties of this table viewer. The properties must
	 * correspond with the columns of the table control. They are used to
	 * identify the column in a cell modifier.
	 * 
	 * @return the list of column properties
	 */
	public Object[] getColumnProperties() {
		return tableViewerImpl.getColumnProperties();
	}

	/*
	 *  (non-Javadoc)
	 * @see org.eclipse.jface.viewers.Viewer#getControl()
	 */
	public Control getControl() {
		return table;
	}

	/**
	 * Returns the element with the given index from this table viewer. Returns
	 * <code>null</code> if the index is out of range.
	 * <p>
	 * This method is internal to the framework.
	 * </p>
	 * 
	 * @param index
	 *            the zero-based index
	 * @return the element at the given index, or <code>null</code> if the
	 *         index is out of range
	 */
	public Object getElementAt(int index) {
		if (index >= 0 && index < table.getItemCount()) {
			TableItem i = table.getItem(index);
			if (i != null) {
				return i.getData();
			}
		}
		return null;
	}

	/**
	 * The table viewer implementation of this <code>Viewer</code> framework
	 * method returns the label provider, which in the case of table viewers
	 * will be an instance of either <code>ITableLabelProvider</code> or
	 * <code>ILabelProvider</code>. If it is an
	 * <code>ITableLabelProvider</code>, then it provides a separate label
	 * text and image for each column. If it is an <code>ILabelProvider</code>,
	 * then it provides only the label text and image for the first column, and
	 * any remaining columns are blank.
	 */
	public IBaseLabelProvider getLabelProvider() {
		return super.getLabelProvider();
	}

	/*
	 *  (non-Javadoc)
	 * @see org.eclipse.jface.viewers.StructuredViewer#getSelectionFromWidget()
	 */
	protected List getSelectionFromWidget() {
		if(virtualManager != null) {
			return getVirtualSelection();
		}
		Widget[] items = table.getSelection();
		ArrayList list = new ArrayList(items.length);
		for (int i = 0; i < items.length; i++) {
			Widget item = items[i];
			Object e = item.getData();
			if (e != null) {
				list.add(e);
			}
		}
		return list;
	}
	
	/**
	 * Get the virtual selection. Avoid calling SWT whenever possible
	 * to prevent extra widget creation.
	 * @return List of Object
	 */

	private List getVirtualSelection() {
		
		List result = new ArrayList();
		int[] selectionIndices = getTable().getSelectionIndices(); 
		if(getContentProvider() instanceof ILazyContentProvider){
			ILazyContentProvider lazy = (ILazyContentProvider) getContentProvider();
			for (int i = 0; i < selectionIndices.length; i++) {
				int selectionIndex = selectionIndices[i];
				lazy.updateElement(selectionIndex);//Start the update
				Object element = getTable().getItem(selectionIndex).getData();
				//Only add the element if it got updated.
				//If this is done deferred the selection will
				//be incomplete until selection is finished.
				if (element != null) {
					result.add(element);
				}				
			}
		}
		else{
			for (int i = 0; i < selectionIndices.length; i++) {
				Object element = null;
				//See if it is cached
				int selectionIndex = selectionIndices[i];
				if (selectionIndex < virtualManager.cachedElements.length){
					element = virtualManager.cachedElements[selectionIndex];
				}
				if (element == null){
					// Not cached so try the item's data
					TableItem item = getTable().getItem(selectionIndex);
					element = item.getData();
				}
				if (element != null) {
					result.add(element);
				}				
			}
			
			
		}
		return result;
	}

	/**
	 * Returns this table viewer's table control.
	 * 
	 * @return the table control
	 */
	public Table getTable() {
		return table;
	}

	/*
	 *  (non-Javadoc)
	 * @see org.eclipse.jface.viewers.ContentViewer#hookControl(org.eclipse.swt.widgets.Control)
	 */
	protected void hookControl(Control control) {
		super.hookControl(control);
		Table tableControl = (Table) control;
		tableControl.addMouseListener(new MouseAdapter() {
			public void mouseDown(MouseEvent e) {
				tableViewerImpl.handleMouseDown(e);
			}
		});
	}

	/*
	 * Returns the index where the item should be inserted.
	 */
	protected int indexForElement(Object element) {
		ViewerComparator comparator = getComparator();
		if (comparator == null) {
			return table.getItemCount();
		}
		int count = table.getItemCount();
		int min = 0, max = count - 1;
		while (min <= max) {
			int mid = (min + max) / 2;
			Object data = table.getItem(mid).getData();
			int compare = comparator.compare(this, data, element);
			if (compare == 0) {
				// find first item > element
				while (compare == 0) {
					++mid;
					if (mid >= count) {
						break;
					}
					data = table.getItem(mid).getData();
					compare = comparator.compare(this, data, element);
				}
				return mid;
			}
			if (compare < 0) {
				min = mid + 1;
			} else {
				max = mid - 1;
			}
		}
		return min;
	}

	/**
	 * Initializes the table viewer implementation.
	 */
	private void initTableViewerImpl() {
		tableViewerImpl = new TableEditorImpl(this) {
			Rectangle getBounds(Item item, int columnNumber) {
				return ((TableItem) item).getBounds(columnNumber);
			}

			int getColumnCount() {
				return getTable().getColumnCount();
			}

			Item[] getSelection() {
				return getTable().getSelection();
			}

			void setEditor(Control w, Item item, int columnNumber) {
				tableEditor.setEditor(w, (TableItem) item, columnNumber);
			}

			void setSelection(StructuredSelection selection, boolean b) {
				TableViewer.this.setSelection(selection, b);
			}

			void showSelection() {
				getTable().showSelection();
			}

			void setLayoutData(CellEditor.LayoutData layoutData) {
				tableEditor.grabHorizontal = layoutData.grabHorizontal;
				tableEditor.horizontalAlignment = layoutData.horizontalAlignment;
				tableEditor.minimumWidth = layoutData.minimumWidth;
			}

			void handleDoubleClickEvent() {
				Viewer viewer = getViewer();
				fireDoubleClick(new DoubleClickEvent(viewer, viewer
						.getSelection()));
				fireOpen(new OpenEvent(viewer, viewer.getSelection()));
			}
		};
	}

	/*
	 *  (non-Javadoc)
	 * @see org.eclipse.jface.viewers.Viewer#inputChanged(java.lang.Object, java.lang.Object)
	 */
	protected void inputChanged(Object input, Object oldInput) {
		getControl().setRedraw(false);
		try {
			// refresh() attempts to preserve selection, which we want here
			refresh();
		} finally {
			getControl().setRedraw(true);
		}
	}

	/**
	 * Inserts the given element into this table viewer at the given position.
	 * If this viewer has a sorter, the position is ignored and the element is
	 * inserted at the correct position in the sort order.
	 * <p>
	 * This method should be called (by the content provider) when elements have
	 * been added to the model, in order to cause the viewer to accurately
	 * reflect the model. This method only affects the viewer, not the model.
	 * </p>
	 * 
	 * @param element
	 *            the element
	 * @param position
	 *            a 0-based position relative to the model, or -1 to indicate
	 *            the last position
	 */
	public void insert(Object element, int position) {
		tableViewerImpl.applyEditorValue();
		if (getComparator() != null || hasFilters()) {
			add(element);
			return;
		}
		if (position == -1) {
			position = table.getItemCount();
		}
		
		createItem(element,position);
	}

	/*
	 *  (non-Javadoc)
	 * @see org.eclipse.jface.viewers.StructuredViewer#internalRefresh(java.lang.Object)
	 */
	protected void internalRefresh(Object element) {
		internalRefresh(element, true);
	}

	/*
	 *  (non-Javadoc)
	 * @see org.eclipse.jface.viewers.StructuredViewer#internalRefresh(java.lang.Object, boolean)
	 */
	protected void internalRefresh(Object element, boolean updateLabels) {
		tableViewerImpl.applyEditorValue();
		if (element == null || equals(element, getRoot())) {
			if(virtualManager == null) {
				internalRefreshAll(updateLabels);
			} else{
				internalVirtualRefreshAll();
			}
		} else {
			Widget w = findItem(element);
			if (w != null) {
				updateItem(w, element);
			}
		}
	}

	/**
	 * Refresh all with virtual elements.
	 * 
	 * @since 3.1
	 */
	private void internalVirtualRefreshAll() {
		
		Object root = getRoot();
		IContentProvider contentProvider = getContentProvider();
		
		//Invalidate for lazy
		if(!(contentProvider instanceof ILazyContentProvider) 
				&& (contentProvider instanceof IStructuredContentProvider)) {
			//Don't cache if the root is null but cache if it is not lazy.
			if(root != null){
				virtualManager.cachedElements = 
					((IStructuredContentProvider) getContentProvider()).getElements(root);
				getTable().setItemCount(virtualManager.cachedElements.length);
			}
		}
		getTable().clearAll();
	}

	/**
	 * Refresh all of the elements of the table. update the
	 * labels if updatLabels is true;
	 * @param updateLabels
	 * 
	 * @since 3.1
	 */
	private void internalRefreshAll(boolean updateLabels) {
		// the parent

		// in the code below, it is important to do all disassociates
		// before any associates, since a later disassociate can undo an
		// earlier associate
		// e.g. if (a, b) is replaced by (b, a), the disassociate of b to
		// item 1 could undo
		// the associate of b to item 0.

		Object[] children = getSortedChildren(getRoot());
		TableItem[] items = getTable().getItems();
		int min = Math.min(children.length, items.length);
		for (int i = 0; i < min; ++i) {
			
			
			TableItem item = items[i];
				
			// if the element is unchanged, update its label if appropriate
			if (equals(children[i], item.getData())) {
				if (updateLabels) {
					updateItem(item, children[i]);
				} else {
					// associate the new element, even if equal to the old
					// one,
					// to remove stale references (see bug 31314)
					associate(children[i], item);
				}
			} else {
				// updateItem does an associate(...), which can mess up
				// the associations if the order of elements has changed.
				// E.g. (a, b) -> (b, a) first replaces a->0 with b->0, then
				// replaces b->1 with a->1, but this actually removes b->0.
				// So, if the object associated with this item has changed,
				// just disassociate it for now, and update it below.
				item.setText(""); //$NON-NLS-1$
				item.setImage(new Image[Math.max(1,table.getColumnCount())]);//Clear all images
				disassociate(item);
			}
		}
		// dispose of all items beyond the end of the current elements
		if (min < items.length) {
			for (int i = items.length; --i >= min;) {
				
				disassociate(items[i]);
			}
			table.remove(min, items.length - 1);
		}
		// Workaround for 1GDGN4Q: ITPUI:WIN2000 - TableViewer icons get
		// scrunched
		if (table.getItemCount() == 0) {
			table.removeAll();
		}
		// Update items which were removed above
		for (int i = 0; i < min; ++i) {
							
			TableItem item = items[i];
			if (item.getData() == null) {
				updateItem(item, children[i]);
			}
		}
		// add any remaining elements
		for (int i = min; i < children.length; ++i) {
			createItem(children[i],i);
		}
	}


	/**
	 * Removes the given elements from this table viewer.
	 * 
	 * @param elements
	 *            the elements to remove
	 */
	private void internalRemove(final Object[] elements) {
		Object input = getInput();
		for (int i = 0; i < elements.length; ++i) {
			if (equals(elements[i], input)) {
				setInput(null);
				return;
			}
		}
		// use remove(int[]) rather than repeated TableItem.dispose() calls
		// to allow SWT to optimize multiple removals
		int[] indices = new int[elements.length];
		int count = 0;
		for (int i = 0; i < elements.length; ++i) {
			Widget w = findItem(elements[i]);
			if (w instanceof TableItem) {
				TableItem item = (TableItem) w;
				disassociate(item);
				indices[count++] = table.indexOf(item);
			}
		}
		if (count < indices.length) {
			System.arraycopy(indices, 0, indices = new int[count], 0, count);
		}
		table.remove(indices);

		// Workaround for 1GDGN4Q: ITPUI:WIN2000 - TableViewer icons get
		// scrunched
		if (table.getItemCount() == 0) {
			table.removeAll();
		}
	}

	/**
	 * Returns whether there is an active cell editor.
	 * 
	 * @return <code>true</code> if there is an active cell editor, and
	 *         <code>false</code> otherwise
	 */
	public boolean isCellEditorActive() {
		return tableViewerImpl.isCellEditorActive();
	}

	/**
	 * Removes the given elements from this table viewer. The selection is
	 * updated if required.
	 * <p>
	 * This method should be called (by the content provider) when elements have
	 * been removed from the model, in order to cause the viewer to accurately
	 * reflect the model. This method only affects the viewer, not the model.
	 * </p>
	 * 
	 * @param elements
	 *            the elements to remove
	 */
	public void remove(final Object[] elements) {
		assertElementsNotNull(elements);
        if (elements.length == 0) {
        	return;
        }
		preservingSelection(new Runnable() {
			public void run() {
				internalRemove(elements);
			}
		});
	}

	/**
	 * Removes the given element from this table viewer. The selection is
	 * updated if necessary.
	 * <p>
	 * This method should be called (by the content provider) when a single
	 * element has been removed from the model, in order to cause the viewer to
	 * accurately reflect the model. This method only affects the viewer, not
	 * the model. Note that there is another method for efficiently processing
	 * the simultaneous removal of multiple elements.
	 * </p>
	 * <strong>NOTE:</strong> removing an object from a virtual
	 * table will decrement the itemCount.
	 * 
	 * @param element
	 *            the element
	 */
	public void remove(Object element) {
		remove(new Object[] { element });
	}

	/*
	 *  (non-Javadoc)
	 * @see org.eclipse.jface.viewers.StructuredViewer#reveal(java.lang.Object)
	 */
	public void reveal(Object element) {
		Assert.isNotNull(element);
		Widget w = findItem(element);
		if (w instanceof TableItem) {
			getTable().showItem((TableItem) w);
		}
	}

	/**
	 * Sets the cell editors of this table viewer.
	 * 
	 * @param editors
	 *            the list of cell editors
	 */
	public void setCellEditors(CellEditor[] editors) {
		tableViewerImpl.setCellEditors(editors);
	}

	/**
	 * Sets the cell modifier of this table viewer.
	 * 
	 * @param modifier
	 *            the cell modifier
	 */
	public void setCellModifier(ICellModifier modifier) {
		tableViewerImpl.setCellModifier(modifier);
	}

	/**
	 * Sets the column properties of this table viewer. The properties must
	 * correspond with the columns of the table control. They are used to
	 * identify the column in a cell modifier.
	 * 
	 * @param columnProperties
	 *            the list of column properties
	 */
	public void setColumnProperties(String[] columnProperties) {
		tableViewerImpl.setColumnProperties(columnProperties);
	}

	/**
	 * The table viewer implementation of this <code>Viewer</code> framework
	 * method ensures that the given label provider is an instance of either
	 * <code>ITableLabelProvider</code> or <code>ILabelProvider</code>.
	 * <p>
	 * If the label provider is an {@link ITableLabelProvider}, then it
	 * provides a separate label text and image for each column. Implementers of
	 * <code>ITableLabelProvider</code> may also implement
	 * {@link ITableColorProvider} and/or {@link ITableFontProvider} to provide
	 * colors and/or fonts.
	 * </p>
	 * <p>
	 * If the label provider is an <code>ILabelProvider</code>, then it
	 * provides only the label text and image for the first column, and any
	 * remaining columns are blank. Implementers of <code>ILabelProvider</code>
	 * may also implement {@link IColorProvider} and/or {@link IFontProvider} to
	 * provide colors and/or fonts.
	 * </p>
	 */
	public void setLabelProvider(IBaseLabelProvider labelProvider) {
		Assert.isTrue(labelProvider instanceof ITableLabelProvider
				|| labelProvider instanceof ILabelProvider);
		super.setLabelProvider(labelProvider);
		if(labelProvider instanceof ITableFontProvider || labelProvider instanceof ITableColorProvider) {
			tableColorAndFont = new TableColorAndFontCollector(labelProvider);
		} else {
			tableColorAndFont = new TableColorAndFontNoOp();
		}
				
	}
	
	/**
	 * <p>
	 * Sets a new selection for this viewer and optionally makes it visible.
	 * The TableViewer implmentation of this method is ineffecient for the
	 * ILazyContentProvider as lookup is done by indices rather than elements
	 * and may require population of the entire table in worse case. 
	 * </p>
	 * <p>
	 * Use Table#setSelection(int[] indices) and Table#showSelection() if
	 * you wish to set selection more effeciently when using a ILazyContentProvider.
	 * </p>
	 * 
	 * @param selection the new selection
     * @param reveal <code>true</code> if the selection is to be made
     *   visible, and <code>false</code> otherwise
	 * @see Table#setSelection(int[])
	 * @see Table#showSelection()
	 */
	public void setSelection(ISelection selection, boolean reveal) {
		super.setSelection(selection, reveal);
	}

	/*
	 *  (non-Javadoc)
	 * @see org.eclipse.jface.viewers.StructuredViewer#setSelectionToWidget(java.util.List, boolean)
	 */
	protected void setSelectionToWidget(List list, boolean reveal) {
		
		if (list == null) {
			table.deselectAll();
			return;
		}
		
		if(virtualManager != null){
			virtualSetSelectionToWidget(list, reveal);
			return;
		}
		
		int size = list.size();
		TableItem[] items = new TableItem[size];
		int count = 0;
		for (int i = 0; i < size; ++i) {
			Object o = list.get(i);
			Widget w = findItem(o);
			if (w instanceof TableItem) {
				TableItem item = (TableItem) w;
				items[count++] = item;
			}
		}
		if (count < size) {
			System.arraycopy(items, 0, items = new TableItem[count], 0, count);
		}
		table.setSelection(items);

		if (reveal) {
			table.showSelection();
		}
			
	}
	
	
	/**
	 * Set the selection on a virtual table
	 * @param list The elements to set
	 * @param reveal Whether or not reveal the first item.
	 */
	private void virtualSetSelectionToWidget(List list, boolean reveal) {
		int size = list.size();
		int[] indices = new int[list.size()];
		
		TableItem firstItem = null;
		int count = 0;
		HashSet virtualElements = new HashSet(); 
		for (int i = 0; i < size; ++i) {
			Object o = list.get(i);
			Widget w = findItem(o);
			if (w instanceof TableItem) {
				TableItem item = (TableItem) w;
				indices[count++] = getTable().indexOf(item);
				if (firstItem == null) {
					firstItem = item;
				}
			} else {
				virtualElements.add(o);
			}
		}
		
		if(getContentProvider() instanceof ILazyContentProvider){
			ILazyContentProvider provider = 
				(ILazyContentProvider) getContentProvider();
		
			//Now go through it again until all is done or we are no longer virtual
			//This may create all items so it is not a good
			//idea in general.
			//Use #setSelection (int [] indices,boolean reveal) instead
			for (int i = 0; virtualElements.size() > 0 && i < getTable().getItemCount(); i++) {
				provider.updateElement(i);
				TableItem item = getTable().getItem(i);
				if(virtualElements.contains(item.getData())){
					indices[count++] = i;	
					virtualElements.remove(item.getData());
					if (firstItem == null) {
						firstItem = item;
					}
				}
			}
		}
		else{
			
			if(count != list.size()){//As this is expensive skip it if all have been found
				//If it is not lazy we can use the cache
				for (int i = 0; i < virtualManager.cachedElements.length; i++) {
					Object element = virtualManager.cachedElements[i];
					if(virtualElements.contains(element)){
						TableItem item = getTable().getItem(i);
						item.getText();//Be sure to fire the update
						indices[count++] = i;	
						virtualElements.remove(element);
						if (firstItem == null) {
							firstItem = item;
						}
					}
				}
			}
		}
		
		if (count < size) {
			System.arraycopy(indices, 0, indices = new int[count], 0, count);
		}
		table.setSelection(indices);

		if (reveal && firstItem != null) {
			table.showItem(firstItem);
		}
	}

	/**
	 * Set the item count of the receiver.
	 * @param count the new table size.
	 * 
	 * @since 3.1
	 */
	public void setItemCount(int count){
		getTable().setItemCount(count);
		getTable().redraw();
	}
	
	/**
	 * Replace the entries starting at index with elements.
	 * This method assumes all of these values are correct
	 * and will not call the content provider to verify.
	 * <strong>Note that this method will create a TableItem
	 * for all of the elements provided</strong>.
	 * @param element
	 * @param index
	 * @see ILazyContentProvider
	 * 
	 * @since 3.1
	 */
	public void replace(Object element, int index){
		TableItem item = getTable().getItem(index);
		refreshItem(item, element);
	}

	/**
	 * Clear the table item at the specified index
	 * @param index the index of the table item to be cleared
	 * 
	 * @since 3.1
	 */
	public void clear(int index) {
		TableItem item = getTable().getItem(index);
		if (item.getData() != null) {
			disassociate(item);
		}
		table.clear(index);
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.jface.viewers.StructuredViewer#getRawChildren(java.lang.Object)
	 */
	protected Object[] getRawChildren(Object parent) {

		Assert.isTrue(!(getContentProvider() instanceof ILazyContentProvider),"Cannot get raw children with an ILazyContentProvider");//$NON-NLS-1$
		return super.getRawChildren(parent);
	
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.jface.viewers.StructuredViewer#assertContentProviderType(org.eclipse.jface.viewers.IContentProvider)
	 */
	protected void assertContentProviderType(IContentProvider provider) {
		Assert.isTrue(provider instanceof IStructuredContentProvider ||
				provider instanceof ILazyContentProvider);
	}
	
	
	
}

