public void testAddRemoveListener() { final TextMatcherEditor<String> textMatcherEditor = new TextMatcherEditor<String>(); MatcherEditor<String> weakMatcherEditor = Matchers.weakReferenceProxy(textMatcherEditor); final CountingMatcherEditorListener<String> counter = new CountingMatcherEditorListener<String>(); weakMatcherEditor.addMatcherEditorListener(counter); counter.assertCounterState(0, 0, 0, 0, 0); textMatcherEditor.setFilterText(new String[] {"booblah"}); counter.assertCounterState(0, 0, 0, 1, 0); weakMatcherEditor.removeMatcherEditorListener(counter); textMatcherEditor.setFilterText(new String[] {"bibbety"}); counter.assertCounterState(0, 0, 0, 1, 0); }
public void testGarbageCollectWeakReferenceProxy() { final TextMatcherEditor<String> textMatcherEditor = new TextMatcherEditor<String>(); MatcherEditor<String> weakMatcherEditor = Matchers.weakReferenceProxy(textMatcherEditor); final CountingMatcherEditorListener<String> counter = new CountingMatcherEditorListener<String>(); weakMatcherEditor.addMatcherEditorListener(counter); counter.assertCounterState(0, 0, 0, 0, 0); textMatcherEditor.setFilterText(new String[] {"booblah"}); counter.assertCounterState(0, 0, 0, 1, 0); // removing the only hard reference to weakMatcherEditor should make it gc'able weakMatcherEditor = null; System.gc(); textMatcherEditor.setFilterText(new String[] {"bibbety"}); counter.assertCounterState(0, 0, 0, 1, 0); }
/** * A view for FIX messages that uses a FIXMessageTableFormat and ensures the columns are refreshed * when preferences change. * * @author [email protected] * @author <a href="mailto:[email protected]">Colin DuPlantis</a> */ @ClassVersion("$Id: AbstractFIXMessagesView.java 16543 2013-04-11 16:02:00Z colin $") public abstract class AbstractFIXMessagesView extends MessagesViewBase<ReportHolder> { /** refresher for the view table */ private FIXMessageTableRefresher tableRefresher; /** filter matcher editor used to dynamically filter the table contents */ private final FilterMatcherEditor filterMatcherEditor = new FilterMatcherEditor(); /** default matcher matches all rows */ private static final ca.odell.glazedlists.matchers.Matcher<ReportHolder> DEFAULT_MATCHER = Matchers.trueMatcher(); /** regex determining the search pattern for the view filter */ private static final String FILTER_PATTERN = "([^~=]*)(=|~=)(.*)"; // $NON-NLS-1$ /** * regex pattern object used to split the filter text entered by the user (this is *not* the regex * the user enters) */ private final Pattern mFilterPattern; /** text contents of the filter widget */ private String mFilterText = ""; // $NON-NLS-1$ /** Create a new AbstractFIXMessagesView instance. */ public AbstractFIXMessagesView() { mFilterPattern = Pattern.compile(FILTER_PATTERN); } /* (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); } } /** * Sets the input object upon which the message view is based. * * @param inHistory a <code>FIXMessageHistory</code> value containing the messages to display */ public void setInput(TradeReportsHistory inHistory) { EventList<ReportHolder> list = getMessageList(inHistory); // Get the write lock since it is needed for sorting (see PN-416) Lock writeLock = list.getReadWriteLock().writeLock(); writeLock.lock(); try { super.setInput(new FilterList<ReportHolder>(list, getFilterMatcherEditor())); } finally { writeLock.unlock(); } } /* (non-Javadoc) * @see org.marketcetera.photon.views.MessagesViewBase#dispose() */ @Override public void dispose() { super.dispose(); if (tableRefresher != null) { tableRefresher.dispose(); tableRefresher = null; } } /** * Gets the unique ID of the view. * * @return a <code>String</code> value */ protected abstract String getViewID(); /** * Gets the subset of the given <code>Message</code> history that applies to the implementing * subclass. * * @param inHistory a <code>FIXMessageHistory</code> value * @return the event list of report holders */ protected abstract EventList<ReportHolder> getMessageList(TradeReportsHistory inHistory); /** * Gets the current value of the filter widget. * * @return a <code>String</code> value */ protected final String getFilterText() { return mFilterText; } /** * Gets the dynamic match generator. * * @return a <code>FilterMatcherEditor</code> value */ protected final FilterMatcherEditor getFilterMatcherEditor() { return filterMatcherEditor; } /* * (non-Javadoc) * * @see org.marketcetera.photon.views.MessagesViewBase#initializeToolBar(org.eclipse.jface.action.IToolBarManager) */ @Override protected void initializeToolBar(IToolBarManager inTheToolBarManager) { inTheToolBarManager.add( new ControlContribution(null) { @Override protected Control createControl(Composite parent) { // surround in composite to be able to control the layout Composite composite = new Composite(parent, SWT.NONE); GridLayoutFactory.swtDefaults().applyTo(composite); Label label = new Label(composite, SWT.NONE); label.setText(Messages.FIX_MESSAGE_VIEW_FILTER_LABEL.getText()); GridDataFactory.defaultsFor(label).applyTo(label); return composite; } }); TextContributionItem filterTextContributionItem = new TextContributionItem(""); // $NON-NLS-1$ filterTextContributionItem.addKeyListener( new KeyAdapter() { @Override public void keyReleased(KeyEvent e) { Text theText = (Text) e.widget; setFilterText(theText.getText()); if ('\r' == e.character) { try { handleFilter(mFilterText); } catch (Throwable t) { t.printStackTrace(); } } } }); inTheToolBarManager.add(filterTextContributionItem); // Add execution report button inTheToolBarManager.add(new AddExecutionReportAction()); } /** * The FIXMessageTableFormat manages the addition/removal of columns. The Enum[] columns feature * should not be used to create columns. * * @return always null */ @Override protected Enum<?>[] getEnumValues() { return null; } /** * Creates the table format for the view. * * @param inMessageTable a <code>Table</code> value * @return a <code>FIXMessageTableFormat<MessageHolder></code> value */ protected FIXMessageTableFormat<ReportHolder> createFIXMessageTableFormat(Table inMessageTable) { return new BrokerSupportTableFormat(inMessageTable, getViewID()); } /* (non-Javadoc) * @see org.marketcetera.photon.views.MessagesViewBase#createTableViewer(org.eclipse.swt.widgets.Table, java.lang.Enum<?>[]) */ @Override protected IndexedTableViewer createTableViewer(Table inMessageTable, Enum<?>[] inEnums) { IndexedTableViewer aMessagesViewer = new IndexedTableViewer(inMessageTable); getSite().setSelectionProvider(aMessagesViewer); aMessagesViewer.setContentProvider(new EventListContentProvider<ReportHolder>()); FIXMessageTableFormat<ReportHolder> tableFormat = createFIXMessageTableFormat(inMessageTable); aMessagesViewer.setLabelProvider(tableFormat); tableRefresher = new FIXMessageTableRefresher(aMessagesViewer, tableFormat); createContextMenu(inMessageTable); return aMessagesViewer; } /** * Creates the <code>Matcher</code> to use for the <code>String</code> match filter. * * @param inFixField an <code>int</code> value containing the FIX field against which to match * @param inValue a <code>String</code> value containing the value to match against the table rows * @return a {@link ca.odell.glazedlists.matchers.Matcher}<{@link ReportHolder}> value */ protected ca.odell.glazedlists.matchers.Matcher<ReportHolder> createStringMatcher( int inFixField, String inValue) { return new FIXStringMatcher(inFixField, inValue); } /** * Creates the <code>Matcher</code> to use for the <code>Regex</code> match filter. * * @param inFixField an <code>int</code> value containing the FIX field against which to match * @param inValue a <code>String</code> value containing the value to match against the table rows * @return a <code>ca.odell.glazedlists.matchers.Matcher<MessageHolder></code> value */ protected ca.odell.glazedlists.matchers.Matcher<ReportHolder> createRegexMatcher( int inFixField, String inValue) { return new FIXRegexMatcher(inFixField, inValue); } /** * 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()); } } /** * Gets the default <code>Matcher</code> to use when no filter is specified. * * @return a <code>ca.odell.glazedlists.matchers.Matcher<MessageHolder></code> value */ protected ca.odell.glazedlists.matchers.Matcher<ReportHolder> getDefaultMatcher() { return DEFAULT_MATCHER; } /** * Sets the filter text contents. * * @param inText a <code>String</code> value */ private void setFilterText(String inText) { mFilterText = inText; } /** * Creates the context menu for the given <code>Table</code>. * * @param inTable a <code>Table</code> value */ private void createContextMenu(Table inTable) { ContextMenuFactory contextMenuFactory = new ContextMenuFactory(); contextMenuFactory.createContextMenu( "fixMessageContextMenu", //$NON-NLS-1$ inTable, getSite()); } /** * Coordinates application of <code>Matcher</code> objects to create a filter. * * @author <a href="mailto:[email protected]">Colin DuPlantis</a> * @version $Id: AbstractFIXMessagesView.java 16543 2013-04-11 16:02:00Z colin $ * @since 0.7.0 */ @ClassVersion( "$Id: AbstractFIXMessagesView.java 16543 2013-04-11 16:02:00Z colin $") //$NON-NLS-1$ protected static final class FilterMatcherEditor extends AbstractMatcherEditor<ReportHolder> { /** * Sets the <code>Matcher</code> used in the editor. * * @param inMatcher a <code>ca.odell.glazedlists.matchers.Matcher<MessageHolder></code> * value */ protected final void setMatcher(ca.odell.glazedlists.matchers.Matcher<ReportHolder> inMatcher) { fireChanged(inMatcher); } } }