package org.marketcetera.photon.internal.positions.ui; import java.math.BigDecimal; import java.util.List; import org.apache.commons.lang.Validate; import org.eclipse.jface.util.OpenStrategy; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.ui.IMemento; import org.eclipse.ui.part.Page; import org.marketcetera.core.position.PositionMetrics; import org.marketcetera.core.position.PositionRow; import org.marketcetera.core.position.PositionEngine.PositionData; import org.marketcetera.photon.commons.ui.workbench.ChooseColumnsMenu.IColumnProvider; import org.marketcetera.photon.core.InstrumentPrettyPrinter; import org.marketcetera.photon.positions.ui.IPositionLabelProvider; import org.marketcetera.trade.Currency; import org.marketcetera.trade.Equity; import org.marketcetera.trade.Future; import org.marketcetera.trade.Instrument; import org.marketcetera.trade.Option; import org.marketcetera.util.misc.ClassVersion; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.FilterList; import ca.odell.glazedlists.TextFilterator; import ca.odell.glazedlists.gui.TableFormat; import ca.odell.glazedlists.matchers.SearchEngineTextMatcherEditor; import ca.odell.glazedlists.util.concurrent.ReadWriteLock; /* $License$ */ /** * Abstract base class of pages in the Positions view. * * @author <a href="mailto:will@marketcetera.com">Will Horn</a> * @version $Id: PositionsViewPage.java 16364 2012-11-08 06:13:58Z sameer.patil $ * @since 1.5.0 */ @ClassVersion("$Id: PositionsViewPage.java 16364 2012-11-08 06:13:58Z sameer.patil $") public abstract class PositionsViewPage extends Page implements IColumnProvider { private final PositionsView mView; private final PositionData mPositionData; private final FilterList<PositionRow> mFilteredList; private final SearchEngineTextMatcherEditor<PositionRow> mMatcherEditor; private final ReadWriteLock mLock; private final IMemento mMemento; private Control mControl; /** * Constructor. * * @param view * the view this page is part of, cannot be null * @param memento * the saved page state * @throws IllegalArgumentException * if view is null */ public PositionsViewPage(PositionsView view, IMemento memento) { Validate.notNull(view); mView = view; mMemento = memento; mPositionData = getPositionData(); EventList<PositionRow> positions = mPositionData.getPositions(); mLock = positions.getReadWriteLock(); mLock.writeLock().lock(); try { mFilteredList = new FilterList<PositionRow>(positions); mMatcherEditor = new SearchEngineTextMatcherEditor<PositionRow>( getFilterator()); mFilteredList.setMatcherEditor(mMatcherEditor); } finally { mLock.writeLock().unlock(); } } @Override public void createControl(Composite parent) { mLock.writeLock().lock(); try { mControl = doCreateControl(parent, mMemento); mControl.addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(DisposeEvent e) { controlDisposed(); mFilteredList.dispose(); mPositionData.dispose(); } }); } finally { mLock.writeLock().unlock(); } } /** * Returns the view this page is a part of. * * @return returns the view */ public PositionsView getView() { return mView; } /** * Returns the list of positions, filtered according to the view's filter * box. * * @return returns the position list */ public EventList<PositionRow> getPositions() { return mFilteredList; } /** * Callback when the control returned by {@link #createControl(Composite)} * is disposed. Subclasses can override to perform cleanup. * * Subclasses can also override {@link #dispose()}, but by then the control * and all children are invalid. */ protected void controlDisposed() { // no op } @Override public Control getControl() { return mControl; } @Override public void setFocus() { if (mControl != null && !mControl.isFocusControl()) { mControl.setFocus(); } } /** * Returns the position data that page will display. The base class takes * responsibility for disposing it when the page is closed. * * @return the position data for the page, must not be null */ protected abstract PositionData getPositionData(); /** * Returns the filterator that should be used to control filtering of * position rows. * * @return a text filterator, must not be null */ protected abstract TextFilterator<? super PositionRow> getFilterator(); /** * Creates the control for this positions view page. * * @param parent * the parent composite in which to create the control * @return the new page control, must not be null */ protected abstract Control doCreateControl(Composite parent, IMemento memento); /** * Saves page state. * * @param memento * the memento of saved state */ public abstract void saveState(IMemento memento); /** * Applies a filter text to this page. The page will reduce the number of * items displayed according to the filter text. * * @param filterText * the filter text, should not be null * @throws IllegalArgumentException * if filterText is null */ public void setFilterText(String filterText) { Validate.notNull(filterText); mMatcherEditor.refilter(filterText); } protected static String formatKey(String s) { return s == null ? Messages.POSITIONS_TABLE_EMPTY_KEY__LABEL.getText() : s; } protected static String getTraderName(String s) { if (s != null) { IPositionLabelProvider provider = Activator.getDefault() .getPositionLabelProvider(); if (provider != null) { s = provider.getTraderName(s); } } return s; } protected static String formatValue(Object n) { if (n == null) { return Messages.POSITIONS_TABLE_UNKNOWN_VALUE__LABEL.getText(); } else if (n instanceof BigDecimal) { return ((BigDecimal) n).setScale(2, BigDecimal.ROUND_HALF_UP) .toPlainString(); } else { return n.toString(); } } protected static class BasePositionRowFormat implements TableFormat<PositionRow> { private static final String[] COLUMN_NAMES = new String[] { Messages.POSITIONS_TABLE_INSTRUMENT_COLUMN__HEADING.getText(), Messages.POSITIONS_TABLE_OPTION_ROOT_COLUMN__HEADING.getText(), Messages.POSITIONS_TABLE_OPTION_EXPIRY_COLUMN__HEADING .getText(), Messages.POSITIONS_TABLE_OPTION_TYPE_COLUMN__HEADING.getText(), Messages.POSITIONS_TABLE_OPTION_STRIKE_PRICE_COLUMN__HEADING .getText(), Messages.POSITIONS_TABLE_POSITION_COLUMN__HEADING.getText(), Messages.POSITIONS_TABLE_INCOMING_COLUMN__HEADING.getText(), Messages.POSITIONS_TABLE_POSITION_PL_COLUMN__HEADING.getText(), Messages.POSITIONS_TABLE_TRADING_PL_COLUMN__HEADING.getText(), Messages.POSITIONS_TABLE_REALIZED_PL_COLUMN__HEADING.getText(), Messages.POSITIONS_TABLE_UNREALIZED_PL_COLUMN__HEADING .getText(), Messages.POSITIONS_TABLE_TOTAL_PL_COLUMN__HEADING.getText() }; private int mOffset; public int getColumnCount() { return COLUMN_NAMES.length; } public BasePositionRowFormat(int offset) { mOffset = offset; } public String getColumnName(int column) { return COLUMN_NAMES[column - mOffset]; } /** * @see TableFormat#getColumnValue(java.lang.Object, int) * @throws IllegalArgumentException * if column is not in the expected range */ public Object getColumnValue(PositionRow baseObject, int column) { // TODO: instrument specific functionality that can be abstraced Instrument instrument = baseObject.getInstrument(); Option option = (instrument instanceof Option) ? (Option) instrument : null; Future future = (instrument instanceof Future) ? (Future)instrument : null; PositionMetrics metrics = baseObject.getPositionMetrics(); switch (column - mOffset) { case 0: return getInstrumentLabel(instrument); case 1: if(option != null) { return option.getSymbol(); } if(future != null) { return future.getSymbol(); } return null; case 2: if(option != null) { return InstrumentPrettyPrinter.printOptionExpiry(option); } if(future != null) { return InstrumentPrettyPrinter.printFutureExpiry(future); } return null; case 3: return option == null ? null : option.getType(); case 4: return option == null ? null : option.getStrikePrice(); case 5: return metrics.getPosition(); case 6: return metrics.getIncomingPosition(); case 7: return metrics.getPositionPL(); case 8: return metrics.getTradingPL(); case 9: return metrics.getRealizedPL(); case 10: return metrics.getUnrealizedPL(); case 11: return metrics.getTotalPL(); default: throw new IllegalArgumentException(String.valueOf(column)); } } private String getInstrumentLabel(Instrument instrument) { // TODO: instrument specific functionality that can be abstracted if (instrument instanceof Equity) { return Messages.POSITIONS_TABLE_EQUITY__LABEL.getText(); } else if (instrument instanceof Option) { return Messages.POSITIONS_TABLE_OPTION__LABEL.getText(); } else if (instrument instanceof Future) { return Messages.POSITIONS_TABLE_FUTURE__LABEL.getText(); } else if (instrument instanceof Currency) { return Messages.POSITIONS_TABLE_CURRENCY__LABEL.getText(); } else { return null; } } } protected abstract static class PositionSelectionProvider extends Viewer { private final Control mControl; public PositionSelectionProvider(Control control) { Validate.notNull(control); mControl = control; OpenStrategy handler = new OpenStrategy(control); handler.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { handleSelect(e); } }); } @Override public Control getControl() { return mControl; } @Override public ISelection getSelection() { if (mControl.isDisposed()) { return StructuredSelection.EMPTY; } List<PositionRow> list = getSelectionFromWidget(); return new StructuredSelection(list); } protected abstract List<PositionRow> getSelectionFromWidget(); private void handleSelect(SelectionEvent event) { /* * Handle case where an earlier selection listener disposed the * control. */ if (!mControl.isDisposed()) { updateSelection(getSelection()); } } private void updateSelection(ISelection selection) { SelectionChangedEvent event = new SelectionChangedEvent(this, selection); fireSelectionChanged(event); } @Override public void setSelection(ISelection selection, boolean reveal) { // not used } @Override public void setInput(Object input) { // not using input } @Override public void refresh() { // no op } @Override public Object getInput() { // not using input provider return null; } } }