/* (non-Javadoc)
  * @see org.marketcetera.photon.views.MessagesViewBase#createPartControl(org.eclipse.swt.widgets.Composite)
  */
 @Override
 public void createPartControl(Composite inParent) {
   super.createPartControl(inParent);
   TradeReportsHistory messageHistory = PhotonPlugin.getDefault().getTradeReportsHistory();
   if (messageHistory != null) {
     setInput(messageHistory);
   }
 }
 @Override
 public void runWithEvent(IAction arg0, Event arg1) {
   try {
     ExecutionReport report =
         (ExecutionReport) ((ReportHolder) mSelection.getFirstElement()).getReport();
     processReport(report);
   } catch (Exception e) {
     PhotonPlugin.getMainConsoleLogger().error(Messages.CANNOT_CANCEL.getText(), e);
   }
 }
 /**
  * Adds a new row in the view for the given symbol (if one does not already exist).
  *
  * @param symbol symbol to add to view
  */
 public void addSymbol(final Equity symbol) {
   if (mItemMap.containsKey(symbol)) {
     PhotonPlugin.getMainConsoleLogger().warn(DUPLICATE_SYMBOL.getText(symbol));
   } else {
     busyRun(
         new Runnable() {
           @Override
           public void run() {
             MarketDataViewItem item =
                 new MarketDataViewItem(mMarketDataManager.getMarketData(), symbol);
             mItemMap.put(symbol, item);
             mItems.add(item);
           }
         });
   }
 }
 @Override
 protected void setValue(Object element, Object value) {
   if (StringUtils.isBlank(value.toString())) return;
   final MarketDataViewItem item = (MarketDataViewItem) element;
   final Equity equity = item.getEquity();
   if (equity.getSymbol().equals(value)) return;
   final Equity newEquity = new Equity(value.toString());
   if (mItemMap.containsKey(newEquity)) {
     PhotonPlugin.getMainConsoleLogger().warn(DUPLICATE_SYMBOL.getText(newEquity.getSymbol()));
     return;
   }
   busyRun(
       new Runnable() {
         @Override
         public void run() {
           mItemMap.remove(equity);
           item.setEquity(newEquity);
           mItemMap.put(newEquity, item);
         }
       });
 }
 /**
  * Processes a change in the contents of the filter widget.
  *
  * @param inValue a <code>String</code> value containing the text of the filter widget
  */
 protected final void handleFilter(String inValue) {
   // inValue contains the full text of what the user entered in the filter widget, e.g. Side=B
   if (inValue.length() > 0) {
     // some filter value has been entered, process that value
     Matcher regexMatcher = mFilterPattern.matcher(inValue);
     // if the value is meaningful, continue, otherwise, exit and do nothing (no filtering changes)
     if (regexMatcher.matches()) {
       // the first match of the regex must be the field against which to match
       String fieldSpecifier = regexMatcher.group(1);
       try {
         // the second is the operator (either '=' or '~=' as dictated by the regex pattern)
         String operator = regexMatcher.group(2);
         // the third is the value to match
         String value = regexMatcher.group(3);
         // the field name may be one that we've translated to a shorter or more readable version
         // try to translate it back first
         String fieldNameToUse = FIXFieldLocalizer.readFIXFieldNameFromCache(fieldSpecifier);
         // dig out the field object indicated by the fieldSpecified (a failure will throw a
         // CoreException)
         Field<?> fixField = FIXMessageUtil.getQuickFixFieldFromName(fieldNameToUse);
         // this is the int value of the FIX field which we now know is valid
         int fixFieldInt = fixField.getField();
         // create a matcher depending on the operator entered by the user
         if ("=".equals(operator)) { // $NON-NLS-1$
           // use a straight string matcher
           getFilterMatcherEditor().setMatcher(createStringMatcher(fixFieldInt, value));
         } else {
           // use a regex matcher
           assert "~=".equals(operator); // $NON-NLS-1$
           getFilterMatcherEditor().setMatcher(createRegexMatcher(fixFieldInt, value));
         }
       } catch (Throwable t) {
         PhotonPlugin.getMainConsoleLogger().error(UNRECOGNIZED_FIELD.getText(fieldSpecifier));
       }
     }
   } else {
     // the value entered by the user is of zero length, use the default matcher
     getFilterMatcherEditor().setMatcher(getDefaultMatcher());
   }
 }
/**
 * Market data view.
 *
 * @author <a href="mailto:[email protected]">Will Horn</a>
 * @version $Id: MarketDataView.java 16154 2012-07-14 16:34:05Z colin $
 * @since 1.0.0
 */
@ClassVersion("$Id: MarketDataView.java 16154 2012-07-14 16:34:05Z colin $")
public final class MarketDataView extends ViewPart
    implements IMSymbolListener, IColumnProvider, Messages {

  /** The view ID. */
  public static final String ID = "org.marketcetera.photon.views.MarketDataView"; // $NON-NLS-1$

  private Map<Equity, MarketDataViewItem> mItemMap = new HashMap<Equity, MarketDataViewItem>();

  private TextContributionItem mSymbolEntryText;

  private final IMarketDataManager mMarketDataManager =
      PhotonPlugin.getDefault().getMarketDataManager();

  private TableViewer mViewer;

  private WritableList mItems;

  private IMemento mViewState;

  private Clipboard mClipboard;

  /** Constructor. */
  public MarketDataView() {}

  @Override
  public void init(IViewSite site, IMemento memento) throws PartInitException {
    super.init(site);
    mViewState = memento;
  }

  /**
   * Returns the clipboard for this view.
   *
   * @return the clipboard for this view
   */
  public Clipboard getClipboard() {
    if (mClipboard == null) {
      mClipboard = new Clipboard(mViewer.getControl().getDisplay());
    }
    return mClipboard;
  }

  @Override
  public Table getColumnWidget() {
    return mViewer != null ? mViewer.getTable() : null;
  }

  @Override
  public void createPartControl(Composite parent) {
    final IActionBars actionBars = getViewSite().getActionBars();
    IToolBarManager toolbar = actionBars.getToolBarManager();
    mSymbolEntryText = new TextContributionItem(""); // $NON-NLS-1$
    toolbar.add(mSymbolEntryText);
    toolbar.add(new AddSymbolAction(mSymbolEntryText, this));

    final Table table =
        new Table(parent, SWT.MULTI | SWT.FULL_SELECTION | SWT.V_SCROLL | SWT.BORDER);
    table.setHeaderVisible(true);
    mViewer = new TableViewer(table);
    GridDataFactory.defaultsFor(table).applyTo(table);

    final MarketDataItemComparator comparator = new MarketDataItemComparator();
    mViewer.setComparator(comparator);

    SelectionListener listener =
        new SelectionAdapter() {
          @Override
          public void widgetSelected(SelectionEvent e) {
            // determine new sort column and direction
            TableColumn sortColumn = table.getSortColumn();
            TableColumn currentColumn = (TableColumn) e.widget;
            final int index = table.indexOf(currentColumn);
            int dir = table.getSortDirection();
            if (sortColumn == currentColumn) {
              dir = dir == SWT.UP ? SWT.DOWN : SWT.UP;
            } else {
              table.setSortColumn(currentColumn);
              dir = SWT.UP;
            }
            table.setSortDirection(dir);
            comparator.setSort(dir == SWT.UP ? 1 : -1);
            comparator.setIndex(index);
            mViewer.refresh();
          }
        };

    // create columns, using FIXFieldLocalizer to preserve backwards
    // compatibility
    TableViewerColumn symbolColumn =
        new TableViewerColumn(
            mViewer,
            createColumn(
                table,
                FIXFieldLocalizer.getLocalizedFIXFieldName(Symbol.class.getSimpleName()),
                SWT.LEFT,
                listener));
    symbolColumn.setEditingSupport(new SymbolEditingSupport());
    createColumn(
        table,
        FIXFieldLocalizer.getLocalizedFIXFieldName(LastPx.class.getSimpleName()),
        SWT.RIGHT,
        listener);
    createColumn(
        table,
        FIXFieldLocalizer.getLocalizedFIXFieldName(LastQty.class.getSimpleName()),
        SWT.RIGHT,
        listener);
    createColumn(
        table,
        FIXFieldLocalizer.getLocalizedFIXFieldName(BidSize.class.getSimpleName()),
        SWT.RIGHT,
        listener);
    createColumn(
        table,
        FIXFieldLocalizer.getLocalizedFIXFieldName(BidPx.class.getSimpleName()),
        SWT.RIGHT,
        listener);
    createColumn(
        table,
        FIXFieldLocalizer.getLocalizedFIXFieldName(OfferPx.class.getSimpleName()),
        SWT.RIGHT,
        listener);
    createColumn(
        table,
        FIXFieldLocalizer.getLocalizedFIXFieldName(OfferSize.class.getSimpleName()),
        SWT.RIGHT,
        listener);

    // restore table state if it exists
    if (mViewState != null) {
      ColumnState.restore(table, mViewState);
      for (TableColumn column : table.getColumns()) {
        if (column.getWidth() == 0) {
          column.setResizable(false);
        }
      }
    }

    registerContextMenu();
    getSite().setSelectionProvider(mViewer);

    ObservableListContentProvider content = new ObservableListContentProvider();
    mViewer.setContentProvider(content);
    IObservableSet domain = content.getKnownElements();
    IObservableMap[] maps =
        new IObservableMap[] {
          BeansObservables.observeMap(domain, MarketDataViewItem.class, "symbol"), // $NON-NLS-1$
          createCompositeMap(
              domain, "latestTick", MDPackage.Literals.MD_LATEST_TICK__PRICE), // $NON-NLS-1$
          createCompositeMap(
              domain, "latestTick", MDPackage.Literals.MD_LATEST_TICK__SIZE), // $NON-NLS-1$
          createCompositeMap(
              domain, "topOfBook", MDPackage.Literals.MD_TOP_OF_BOOK__BID_SIZE), // $NON-NLS-1$
          createCompositeMap(
              domain, "topOfBook", MDPackage.Literals.MD_TOP_OF_BOOK__BID_PRICE), // $NON-NLS-1$
          createCompositeMap(
              domain, "topOfBook", MDPackage.Literals.MD_TOP_OF_BOOK__ASK_PRICE), // $NON-NLS-1$
          createCompositeMap(
              domain, "topOfBook", MDPackage.Literals.MD_TOP_OF_BOOK__ASK_SIZE) // $NON-NLS-1$
        };
    mViewer.setLabelProvider(new ObservableMapLabelProvider(maps));
    mViewer.setUseHashlookup(true);
    mItems = WritableList.withElementType(MarketDataViewItem.class);
    mViewer.setInput(mItems);
  }

  private IObservableMap createCompositeMap(
      IObservableSet domain, String property, EStructuralFeature feature) {
    return new CompositeMap(
        BeansObservables.observeMap(domain, MarketDataViewItem.class, property),
        EMFObservables.mapFactory(feature));
  }

  private TableColumn createColumn(
      final Table table, String text, int alignment, SelectionListener listener) {
    final TableColumn column = new TableColumn(table, SWT.NONE);
    column.setWidth(70);
    column.setText(text);
    column.setMoveable(true);
    column.setAlignment(alignment);
    column.addSelectionListener(listener);
    return column;
  }

  /** Register the context menu for the viewer so that commands may be added to it. */
  private void registerContextMenu() {
    MenuManager contextMenu = new MenuManager();
    contextMenu.setRemoveAllWhenShown(true);
    getSite().registerContextMenu(contextMenu, mViewer);
    Control control = mViewer.getControl();
    Menu menu = contextMenu.createContextMenu(control);
    control.setMenu(menu);
  }

  @Override
  public void saveState(IMemento memento) {
    ColumnState.save(getColumnWidget(), memento);
  }

  @Override
  public void setFocus() {
    if (mSymbolEntryText.isEnabled()) mSymbolEntryText.setFocus();
  }

  @Override
  public boolean isListeningSymbol(Equity symbol) {
    return false;
  }

  @Override
  public void onAssertSymbol(Equity symbol) {
    addSymbol(symbol);
  }

  /**
   * Adds a new row in the view for the given symbol (if one does not already exist).
   *
   * @param symbol symbol to add to view
   */
  public void addSymbol(final Equity symbol) {
    if (mItemMap.containsKey(symbol)) {
      PhotonPlugin.getMainConsoleLogger().warn(DUPLICATE_SYMBOL.getText(symbol));
    } else {
      busyRun(
          new Runnable() {
            @Override
            public void run() {
              MarketDataViewItem item =
                  new MarketDataViewItem(mMarketDataManager.getMarketData(), symbol);
              mItemMap.put(symbol, item);
              mItems.add(item);
            }
          });
    }
  }

  private void busyRun(Runnable runnable) {
    BusyIndicator.showWhile(getViewSite().getShell().getDisplay(), runnable);
  }

  private void remove(final MarketDataViewItem item) {
    mItemMap.remove(item.getEquity());
    mItems.remove(item);
    item.dispose();
  }

  @Override
  public void dispose() {
    for (MarketDataViewItem item : mItemMap.values()) {
      item.dispose();
    }
    mItemMap = null;
    mItems = null;
    if (mClipboard != null) {
      mClipboard.dispose();
    }
    super.dispose();
  }

  /**
   * Handles column sorting.
   *
   * @author <a href="mailto:[email protected]">Will Horn</a>
   * @version $Id: MarketDataView.java 16154 2012-07-14 16:34:05Z colin $
   * @since 1.0.0
   */
  @ClassVersion("$Id: MarketDataView.java 16154 2012-07-14 16:34:05Z colin $") // $NON-NLS-1$
  private static class MarketDataItemComparator extends ViewerComparator {

    private int mIndex = -1;
    private int mDirection = 0;

    /** @param index index of sort column */
    public void setIndex(int index) {
      mIndex = index;
    }

    /** @param direction sort direction, 1 for ascending and -1 for descending */
    public void setSort(int direction) {
      mDirection = direction;
    }

    @Override
    public int compare(Viewer viewer, Object e1, Object e2) {
      if (mIndex == -1) return 0;
      MarketDataViewItem item1 = (MarketDataViewItem) e1;
      MarketDataViewItem item2 = (MarketDataViewItem) e2;
      int compare;
      switch (mIndex) {
        case 0:
          String symbol1 = item1.getEquity().getSymbol();
          String symbol2 = item2.getEquity().getSymbol();
          compare = compareNulls(symbol1, symbol2);
          if (compare == 0) {
            compare = symbol1.compareTo(symbol2);
          }
          break;
        case 1:
          BigDecimal tradePrice1 = item1.getLatestTick().getPrice();
          BigDecimal tradePrice2 = item2.getLatestTick().getPrice();
          compare = compareNulls(tradePrice1, tradePrice2);
          if (compare == 0) {
            compare = tradePrice1.compareTo(tradePrice2);
          }
          break;
        case 2:
          BigDecimal tradeSize1 = item1.getLatestTick().getSize();
          BigDecimal tradeSize2 = item2.getLatestTick().getSize();
          compare = compareNulls(tradeSize1, tradeSize2);
          if (compare == 0) {
            compare = tradeSize1.compareTo(tradeSize2);
          }
          break;
        case 3:
          BigDecimal bidSize1 = item1.getTopOfBook().getBidSize();
          BigDecimal bidSize2 = item2.getTopOfBook().getBidSize();
          compare = compareNulls(bidSize1, bidSize2);
          if (compare == 0) {
            compare = bidSize1.compareTo(bidSize2);
          }
          break;
        case 4:
          BigDecimal bidPrice1 = item1.getTopOfBook().getBidPrice();
          BigDecimal bidPrice2 = item2.getTopOfBook().getBidPrice();
          compare = compareNulls(bidPrice1, bidPrice2);
          if (compare == 0) {
            compare = bidPrice1.compareTo(bidPrice2);
          }
          break;
        case 5:
          BigDecimal askPrice1 = item1.getTopOfBook().getAskPrice();
          BigDecimal askPrice2 = item2.getTopOfBook().getAskPrice();
          compare = compareNulls(askPrice1, askPrice2);
          if (compare == 0) {
            compare = askPrice1.compareTo(askPrice2);
          }
          break;
        case 6:
          BigDecimal askSize1 = item1.getTopOfBook().getAskSize();
          BigDecimal askSize2 = item2.getTopOfBook().getAskSize();
          compare = compareNulls(askSize1, askSize2);
          if (compare == 0) {
            compare = askSize1.compareTo(askSize2);
          }
          break;
        default:
          throw new AssertionFailedException("Invalid column index"); // $NON-NLS-1$
      }
      return mDirection * compare;
    }

    private int compareNulls(Object o1, Object o2) {
      if (o1 == null) {
        return (o2 == null) ? 0 : -1;
      }
      if (o2 == null) {
        return (o1 == null) ? 0 : 1;
      }
      return 0;
    }
  }

  /**
   * Provides support for editing symbols in-line
   *
   * @author <a href="mailto:[email protected]">Will Horn</a>
   * @version $Id: MarketDataView.java 16154 2012-07-14 16:34:05Z colin $
   * @since 1.0.0
   */
  @ClassVersion("$Id: MarketDataView.java 16154 2012-07-14 16:34:05Z colin $") // $NON-NLS-1$
  private final class SymbolEditingSupport extends EditingSupport {

    private final TextCellEditor mTextCellEditor;

    private SymbolEditingSupport() {
      super(mViewer);
      this.mTextCellEditor = new TextCellEditor(mViewer.getTable());
    }

    @Override
    protected void setValue(Object element, Object value) {
      if (StringUtils.isBlank(value.toString())) return;
      final MarketDataViewItem item = (MarketDataViewItem) element;
      final Equity equity = item.getEquity();
      if (equity.getSymbol().equals(value)) return;
      final Equity newEquity = new Equity(value.toString());
      if (mItemMap.containsKey(newEquity)) {
        PhotonPlugin.getMainConsoleLogger().warn(DUPLICATE_SYMBOL.getText(newEquity.getSymbol()));
        return;
      }
      busyRun(
          new Runnable() {
            @Override
            public void run() {
              mItemMap.remove(equity);
              item.setEquity(newEquity);
              mItemMap.put(newEquity, item);
            }
          });
    }

    @Override
    protected Object getValue(Object element) {
      return ((MarketDataViewItem) element).getEquity().getSymbol();
    }

    @Override
    protected CellEditor getCellEditor(Object element) {
      return mTextCellEditor;
    }

    @Override
    protected boolean canEdit(Object element) {
      return true;
    }
  }

  /**
   * Handles the delete command for this view.
   *
   * @author <a href="mailto:[email protected]">Will Horn</a>
   * @version $Id: MarketDataView.java 16154 2012-07-14 16:34:05Z colin $
   * @since 1.0.0
   */
  @ClassVersion("$Id: MarketDataView.java 16154 2012-07-14 16:34:05Z colin $")
  public static final class DeleteCommandHandler extends AbstractHandler implements IHandler {

    @Override
    public Object execute(ExecutionEvent event) throws ExecutionException {
      IWorkbenchPart part = HandlerUtil.getActivePartChecked(event);
      ISelection selection = HandlerUtil.getCurrentSelectionChecked(event);
      if (part instanceof MarketDataView && selection instanceof IStructuredSelection) {
        final MarketDataView view = (MarketDataView) part;
        final IStructuredSelection sselection = (IStructuredSelection) selection;
        // this can take some time
        view.busyRun(
            new Runnable() {
              public void run() {
                for (Object obj : sselection.toArray()) {
                  if (obj instanceof MarketDataViewItem) {
                    view.remove((MarketDataViewItem) obj);
                  }
                }
              }
            });
      }
      return null;
    }
  }

  /**
   * Handles the copy command for this view
   *
   * @author <a href="mailto:[email protected]">Will Horn</a>
   * @version $Id: MarketDataView.java 16154 2012-07-14 16:34:05Z colin $
   * @since 1.0.0
   */
  @ClassVersion("$Id: MarketDataView.java 16154 2012-07-14 16:34:05Z colin $") // $NON-NLS-1$
  public static final class CopyCommandHandler extends AbstractHandler implements IHandler {

    @Override
    public Object execute(ExecutionEvent event) throws ExecutionException {
      IWorkbenchPart part = HandlerUtil.getActivePartChecked(event);
      ISelection selection = HandlerUtil.getCurrentSelectionChecked(event);
      if (part instanceof MarketDataView && selection instanceof IStructuredSelection) {
        MarketDataView view = (MarketDataView) part;
        IStructuredSelection sselection = (IStructuredSelection) selection;
        StringBuilder builder = new StringBuilder();
        for (Object obj : sselection.toArray()) {
          if (obj instanceof MarketDataViewItem) {
            MarketDataViewItem item = (MarketDataViewItem) obj;
            builder.append(item);
            builder.append(System.getProperty("line.separator")); // $NON-NLS-1$
          }
        }
        view.getClipboard()
            .setContents(
                new Object[] {builder.toString()}, new Transfer[] {TextTransfer.getInstance()});
      }
      return null;
    }
  }
}