/* * Copyright (c) 2004,2009 Declarative Engineering LLC. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Declarative Engineering LLC * verson 1 which accompanies this distribution, and is available at * http://declarativeengineering.com/legal/DE_Developer_License_v1.txt */ package com.foundation.tcv.swt.server; import com.common.util.ICollection; import com.common.util.IIterator; import com.common.util.IList; import com.common.util.LiteList; import com.common.util.optimized.IntArray; import com.foundation.tcv.swt.ITreeComponent; import com.foundation.tcv.swt.server.SimpleTreeTable.SimpleNodeData; import com.foundation.tcv.view.IAbstractRemoteViewComponent; import com.foundation.tcv.view.ViewMessage; import com.foundation.view.IAbstractComponent; /* * Tries to push as much common tree component code down to a reusable base class. */ public abstract class TreeComponent extends CollectionComponent implements ITreeComponent { /** The collection of columns (AbstractColumn) in server order. */ private IList tableColumns = new LiteList(16); /** * Encapsulates all the data pertaining to a single column in the table component. */ public static abstract class AbstractColumn implements IAbstractComponent, IAbstractRemoteViewComponent { /** * Registers an item in the collection with this column. *
An item can be registered more than once, but must be unregistered just as many times.
* @param rowObject The row to be registered. */ protected void registerItem(RowObject rowObject) { }//registerItem()// /** * Unregisters an item that had been previously registered. * @param rowObject The row to be unregistered. */ protected void unregisterItem(RowObject rowObject) { }//unregisterItem()// /** * Unregisters all items that had been previously registered. */ protected void unregisterAllItems() { }//unregisterAllItems()// /** * Refreshes the value for the cell(s) denoted by the item representing the row(s) and this column. * @param item The item that represents the row(s). * @return Whether the cell data was altered by the refresh. */ protected abstract boolean refreshCellData(Object item); /** * Gets the value for the cell(s) denoted by the item representing the row(s) and this column. * @param item The item that represents the row(s). * @return The cell value. */ protected abstract Object getCellData(Object item); /** * Gets the column's creation index. * @return The index assigned to this column and used to facilitate communication with the actual column resource in the display. */ protected abstract int getIndex(); /** * Sets the column's creation index. * @param index The index assigned to this column and used to facilitate communication with the actual column resource in the display. */ protected abstract void setIndex(int index); /** * Initializes the column before the table component is initialized. */ protected abstract void initialize(); /** * Refreshes the column. */ protected abstract void refresh(); /** * Initializes the column before the table component is released. */ protected abstract void release(); /* (non-Javadoc) * @see com.foundation.tcv.view.IAbstractViewComponent#processMessage(com.foundation.tcv.view.ViewMessage) */ public Object processMessage(ViewMessage viewMessage) { return null; }//processMessage()// }//AbstractColumn// /** * TreeComponent constructor. * @param parent The parent container for this component. * @param name The name of the component. */ public TreeComponent(Container parent, String name, int style) { super(parent, name, style); }//TreeComponent()// /** * Adds a column to the component. * @param column The column to be added. */ protected void addColumn(AbstractColumn column) { tableColumns.add(column.getIndex(), column); //Update the other column indices.// for(int index = column.getIndex() + 1; index < tableColumns.getSize(); index++) { ((AbstractColumn) tableColumns.get(index)).setIndex(index); }//for// //Notify the client.// sendMessage(MESSAGE_ADD_COLUMN, new Integer(column.getIndex())); //TODO: Support columns added after the display is showing.// if(isInitialized()) { //Note: While it would be nice to send the header and cell data, we can't because the column hasn't been setup yet.// //TODO: Send the initial header data.// //TODO: Need to send the initial cell data.// }//if// }//addColumn()// /** * Gets the number of columns in the table. * @return The count of columns. */ protected int getColumnCount() { return tableColumns.getSize(); }//getColumnCount()// /** * Gets the column at the given index. * @param index The zero based column index. * @return The column at the given index. */ protected AbstractColumn getColumn(int index) { return (AbstractColumn) tableColumns.get(index); }//getColumn()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.CollectionComponent#internalViewRefreshCollection(com.common.util.ICollection, com.common.util.ICollection) */ protected void internalViewRefreshCollection(ICollection newCollection, ICollection oldCollection) { sendMessage(MESSAGE_SAVE_VIEW_STATE, null); //Clear the old data.// primaryCollectionItemsRemoved(); //Rebuild the tree.// if(newCollection != null) { IIterator itemIterator = newCollection.iterator(); //Get the array of strings representing the list contents.// while(itemIterator.hasNext()) { Object item = itemIterator.next(); placeItem(null, item); }//while// }//if// //If the selection was invalid previously then refresh the selection since it may no longer be invalid.// if(isSelectionInvalid) { internalViewRefreshSelection(); }//if// sendMessage(MESSAGE_RESTORE_VIEW_STATE, null); }//internalViewRefreshCollection()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.CollectionComponent#internalViewRefreshSelection(java.lang.Object) */ protected void internalViewRefreshSelection(Object selectedItem) { if(selectedItem != null) { RowObject rowObject = (RowObject) getRowObject(selectedItem); if(rowObject == null) { isSelectionInvalid = true; sendMessage(MESSAGE_SET_SELECTION, null); }//if// else { isSelectionInvalid = false; sendMessage(MESSAGE_SET_SELECTION, new int[] {rowObject.objectId}); }//else// }//if// else { //No selection.// sendMessage(MESSAGE_SET_SELECTION, null); }//else// }//internalViewRefreshSelection()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.CollectionComponent#internalViewRefreshSelections(com.common.util.ICollection, com.common.util.ICollection) */ protected void internalViewRefreshSelections(ICollection newSelections, ICollection oldSelections) { if(newSelections != null) { IntArray selectionObjectIds = new IntArray(newSelections.getSize()); IIterator selectionIterator = newSelections.iterator(); //Apply differences between the selection collection and the control selection. Also remove all impossible selections.// while(selectionIterator.hasNext()) { Object selection = selectionIterator.next(); RowObject rowObject = (RowObject) getRowObject(selection); if(rowObject == null) { //An invalid selection because the selection is not in the collection of displayed values.// selectionIterator.remove(); }//if// else { selectionObjectIds.add(rowObject.objectId); }//else// }//while// sendMessage(MESSAGE_SET_SELECTION, selectionObjectIds.toArray()); }//if// else { //Remove all selections.// sendMessage(MESSAGE_REMOVE_SELECTIONS, null); }//else// }//internalViewRefreshSelections()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.CollectionComponent#internalViewInitialize() */ protected void internalViewInitialize() { //Initialize all the columns.// for(int columnIndex = 0; columnIndex < tableColumns.getSize(); columnIndex++) { ((AbstractColumn) tableColumns.get(columnIndex)).initialize(); }//for// super.internalViewInitialize(); }//internalViewInitialize()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.CollectionComponent#internalViewRefresh() */ protected void internalViewRefresh() { super.internalViewRefresh(); //Refresh all the columns.// for(int columnIndex = 0; columnIndex < tableColumns.getSize(); columnIndex++) { ((AbstractColumn) tableColumns.get(columnIndex)).refresh(); }//for// }//internalViewRefresh()// /* (non-Javadoc) * @see com.foundation.view.swt.AbstractComponent#internalViewRelease() */ protected void internalViewRelease() { //Release all the columns.// for(int columnIndex = 0; columnIndex < tableColumns.getSize(); columnIndex++) { ((AbstractColumn) tableColumns.get(columnIndex)).release(); }//for// super.internalViewRelease(); }//internalViewRelease()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.CollectionComponent#itemSorted(int[]) */ protected void itemSorted(int[] mapping) { //TODO: Should this be implemented? //sendMessage(MESSAGE_ORDER_ROWS, mapping); }//itemSorted()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.CollectionComponent#selectionAdded(java.lang.Object) */ protected void selectionAdded(Object value) { RowObject rowObject = getRowObject(value); if(rowObject != null) { sendMessage(MESSAGE_ADD_SELECTION, null, null, rowObject.objectId, -1); }//if// }//selectionAdded()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.CollectionComponent#selectionRemoved(java.lang.Object) */ protected void selectionRemoved(Object value) { RowObject rowObject = getRowObject(value); if(rowObject != null) { sendMessage(MESSAGE_REMOVE_SELECTION, null, null, rowObject.objectId, -1); }//if// }//selectionRemoved()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.CollectionComponent#selectionAllRemoved() */ protected void selectionAllRemoved() { sendMessage(MESSAGE_REMOVE_ALL_SELECTIONS, null); }//selectionAllRemoved()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.CollectionComponent#registerItem(com.foundation.tcv.swt.server.RowObject) */ protected void registerItem(RowObject rowObject) { super.registerItem(rowObject); //Add the item to each of the columns so we receive updates.// for(int index = 0; index < tableColumns.getSize(); index++) { ((AbstractColumn) tableColumns.get(index)).registerItem(rowObject); }//for// }//registerItem()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.CollectionComponent#unregisterItem(com.foundation.tcv.swt.server.RowObject) */ protected void unregisterItem(RowObject rowObject) { super.unregisterItem(rowObject); //Unregister the value with the columns so we stop receiving updates.// for(int index = 0; index < tableColumns.getSize(); index++) { ((AbstractColumn) tableColumns.get(index)).unregisterItem(rowObject); }//for// }//unregisterItem()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.CollectionComponent#unregisterItems() */ protected void unregisterItems() { super.unregisterItems(); //Unregister the items from the columns so we stop recieving updates.// for(int index = 0; index < tableColumns.getSize(); index++) { ((AbstractColumn) tableColumns.get(index)).unregisterAllItems(); }//for// }//unregisterItems()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.CollectionComponent#getItemData(com.foundation.tcv.swt.server.RowObject) */ protected Object getItemData(RowObject rowObject) { Object[] result = new Object[tableColumns.getSize() + 1]; refreshRowHeaderCellData(rowObject.value); result[0] = getRowHeaderCellData(rowObject.value); for(int index = 0; index < result.length; index++) { AbstractColumn column = (AbstractColumn) tableColumns.get(index); if(column != null) { column.refreshCellData(rowObject.value); result[index + 1] = column.getCellData(rowObject.value); }//if// }//for// return result; }//getItemData()// protected void sendAddItemMessage(RowObject rowObject, int itemIndex) { //Don't send duplicate messages since the client doesn't care.// if(rowObject.isFirstReference()) { //TODO: Figure out how to get whether the value can have children from the row object. Send 0 if no children, 1 if can have children. sendMessage(MESSAGE_ADD_ITEM, getItemData(rowObject), getItemHiddenData(rowObject.value), rowObject.objectId, canHaveChildren(rowObject.value) ? 1 : 0); }//if// }//sendAddItemMessage()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.CollectionComponent#sendRemoveItemMessage(com.foundation.tcv.swt.server.CollectionComponent.RowObject, int, boolean) */ protected void sendRemoveItemMessage(RowObject rowObject, int itemIndex, boolean isRemovedEntirely) { if(isRemovedEntirely) { sendMessage(MESSAGE_REMOVE_ITEM, null, null, rowObject.objectId, itemIndex); }//if// }//sendRemoveItemMessage()// /** * Refreshes the row header cell data and determines whether there were substantial alterations. * @param item The row item. * @return Whether the cell data was changed. */ protected abstract boolean refreshRowHeaderCellData(Object item); /** * Gets the row header cell data. * @param item The row item. * @return The cell data for the row's row header. */ protected abstract Object getRowHeaderCellData(Object item); /** * Determines whether the passed item might have children. * @param item The item to check. * @return Whether the item could have children. */ protected abstract boolean canHaveChildren(Object item); /** * Places the item as a child of the given node. It may be grouped so that it is an indirect child of the given node. * @param node The node that will have the item as a child. This may be null if the item is at the root of the tree. * @param item The non-grouping item that is placed under the node. */ protected abstract void placeItem(SimpleNodeData node, Object item); /* (non-Javadoc) * @see com.foundation.tcv.view.IViewComponent#processMessage(com.foundation.tcv.model.ViewMessage) */ public Object processMessage(ViewMessage viewMessage) { Object retVal = null; switch(viewMessage.getMessageNumber()) { case MESSAGE_VIEW_SYNCHRONIZE_SELECTION: { //Receive the selection information from the client.// Object messageData = viewMessage.getMessageData(); if(getAllowMultiSelection()) { int[] selectionObjectIds = (int[]) messageData; boolean[] selectionMarks = new boolean[selectionObjectIds.length]; ICollection modelSelections = getModelSelections(); //Prevent the selection additions from causing a feedback loop.// suspendSelectionHooks = true; try { if(modelSelections != null) { IIterator iterator = modelSelections.iterator(); LiteList removed = new LiteList(modelSelections.getSize()); LiteList added = new LiteList(selectionObjectIds.length); //Initialize the bit field.// for(int index = 0; index < selectionMarks.length; index++) { selectionMarks[index] = false; }//for// //For each previous selection, try to find a matching current selection, otherwise remove it. Mark all used current selections.// while(iterator.hasNext()) { Object next = iterator.next(); RowObject rowObject = getRowObject(next); if(rowObject != null) { int nextObjectId = rowObject.objectId; boolean found = false; for(int index = 0; (!found) && (index < selectionObjectIds.length); index++) { if((selectionObjectIds[index] == nextObjectId) && (!selectionMarks[index])) { selectionMarks[index] = true; found = true; }//if// }//for// //If the selection wasn't found in the view then remove it from the model.// if(!found) { removed.add(next); }//if// //Add the selections that were in the view but not in the model.// for(int index = 0; index < selectionMarks.length; index++) { if(!selectionMarks[index]) { RowObject wrapper = (RowObject) getRowObject(selectionObjectIds[index]); if(wrapper != null) { added.add(wrapper.value); }//if// }//if// }//for// }//if// }//while// modelSelections.replaceAll(removed, added); }//if// }//try// finally { suspendSelectionHooks = false; }//finally// }//if// else { int selectedObjectId = messageData != null ? ((Integer) messageData).intValue() : -1; RowObject selectedRowObject = (RowObject) (selectedObjectId >= 0 ? getRowObject(selectedObjectId) : null); setModelSelection(selectedRowObject == null ? null : selectedRowObject.value); }//else// if(getAutoValidate()) { postSynchronizeValidate(); }//if// break; }//case// default: { retVal = super.processMessage(viewMessage); }//default// }//switch// return retVal; }//processMessage()// }//TreeComponent//