Author: mccaky Date: Mon Nov 3 11:46:24 2014 New Revision: 1425 Log: Added calorimeter filter panel code. Not currently accessible. Added: java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/CrystalFilterPanel.java (with props) java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/util/MapValueIterator.java (with props) Modified: java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/DataFileViewer.java java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/PDataEventViewer.java java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/util/CrystalDataSet.java java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/util/EcalWiringManager.java Added: java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/CrystalFilterPanel.java ============================================================================= --- java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/CrystalFilterPanel.java (added) +++ java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/CrystalFilterPanel.java Mon Nov 3 11:46:24 2014 @@ -0,0 +1,889 @@ +package org.hps.monitoring.ecal.eventdisplay.ui; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.SpringLayout; + +import org.hps.monitoring.ecal.eventdisplay.util.CrystalDataSet; +import org.hps.monitoring.ecal.eventdisplay.util.CrystalDataSet.Motherboard; +import org.hps.monitoring.ecal.eventdisplay.util.CrystalDataSet.Preamplifier; +import org.hps.monitoring.ecal.eventdisplay.util.EcalWiringManager; + +/** + * Class <code>CrystalFilterPanel</code> is an extension of <code> + * JPanel</code> that allows users to select filters for crystals from + * the properties stored in a <code>CrystalDataSet</code> object and + * filter all the crystals present in an <code>EcalWiringManager</code> + * object by said properties.<br/> + * <br/> + * <code>CrystalFilterPanel</code> alerts other classes that a filter + * has been applied through <code>ActionListener</code> objects. + * + * @author Kyle McCarty + * @see JPanel + * @see CrystalDataSet + * @see EcalWiringManager + */ +public final class CrystalFilterPanel extends JPanel { + // Internal variables. + private Dimension preferredSize = null; + private final EcalWiringManager manager; + private List<Point> filteredList = null; + private static final long serialVersionUID = 1L; + private CDSComparator cdsCompare = new CDSComparator(); + private List<ActionListener> alList = new ArrayList<ActionListener>(); + public static final int EVENT_FILTER_APPLIED = ActionEvent.RESERVED_ID_MAX + 10; + public static final int EVENT_FILTER_REMOVED = ActionEvent.RESERVED_ID_MAX + 11; + + // Component variables. + private JCheckBox[] checkActive; + private JComboBox<?>[] comboField; + private JButton buttonApply; + private JButton buttonRemove; + private JButton buttonClear; + private JTextArea textStatus; + + // Component index references and related statics. + private static final int INDEX_X = 0; + private static final int INDEX_Y = 1; + private static final int INDEX_APD = 2; + private static final int INDEX_PREAMP = 3; + private static final int INDEX_LED_CHANNEL = 4; + private static final int INDEX_LED_DRIVER = 5; + private static final int INDEX_FADC_SLOT = 6; + private static final int INDEX_FADC_CHANNEL = 7; + private static final int INDEX_SPLITTER = 8; + private static final int INDEX_HV_GROUP = 9; + private static final int INDEX_JOUT = 10; + private static final int INDEX_MOTHERBOARD = 11; + private static final int INDEX_CHANNEL = 12; + private static final int INDEX_GAIN = 13; + private static final int INDICES = 14; + private static final Color COLOR_CHECK_FONT_ACTIVE = Color.BLACK; + private static final Color COLOR_CHECK_FONT_INACTIVE = Color.GRAY; + private static final String[] FIELD_NAME = { "x-Index", "y-Index", "APD Number", + "Preamp Number", "LED Channel", "LED Driver", "FADC Slot", "FADC Channel", + "Splitter Number", "H.V. Group", "Jout", "Motherboard", "Channel", "Gain" }; + + // Spacing constants. + private static final int hexternal = 15; + private static final int vexternal = 15; + private static final int hinternal = 10; + private static final int vinternal = 5; + + public CrystalFilterPanel(EcalWiringManager dataManager) { + // Store the manager. + manager = dataManager; + + // Create the panel layout. + SpringLayout layout = new SpringLayout(); + setLayout(layout); + + // Instantiate the check boxes. + JCheckBox largestCheck = null; + checkActive = new JCheckBox[INDICES]; + for(int index = 0; index < INDICES; index++) { + // Instantiate the check box. + checkActive[index] = new JCheckBox(FIELD_NAME[index]); + checkActive[index].setSelected(false); + checkActive[index].setForeground(COLOR_CHECK_FONT_INACTIVE); + checkActive[index].addItemListener(new CheckListener(index)); + add(checkActive[index]); + + // Get the largest check box. + if(largestCheck == null) { largestCheck = checkActive[index]; } + else { + if(largestCheck.getPreferredSize().width < checkActive[index].getPreferredSize().width) { + largestCheck = checkActive[index]; + } + } + } + + // Create sets to store all possible entries for each crystal + // data field. + Set<Integer> xIndexSet = new HashSet<Integer>(); + Set<Integer> yIndexSet = new HashSet<Integer>(); + Set<Integer> apdSet = new HashSet<Integer>(); + Set<Preamplifier> preampSet = new HashSet<Preamplifier>(); + Set<Integer> ledChannelSet = new HashSet<Integer>(); + Set<Double> ledDriverSet = new HashSet<Double>(); + Set<Integer> fadcSlotSet = new HashSet<Integer>(); + Set<Integer> fadcChannelSet = new HashSet<Integer>(); + Set<Integer> splitterSet = new HashSet<Integer>(); + Set<Integer> hvGroupSet = new HashSet<Integer>(); + Set<Integer> joutSet = new HashSet<Integer>(); + Set<Integer> channelSet = new HashSet<Integer>(); + Set<Integer> gainSet = new HashSet<Integer>(); + + // Iterate over all crystal data fields to obtain the set of + // all possible entries. + for(CrystalDataSet cds : manager) { + xIndexSet.add(cds.getCrystalXIndex()); + yIndexSet.add(cds.getCrystalYIndex()); + apdSet.add(cds.getAPDNumber()); + preampSet.add(cds.getPreamplifierNumber()); + ledChannelSet.add(cds.getLEDChannel()); + ledDriverSet.add(cds.getLEDDriver()); + fadcSlotSet.add(cds.getFADCSlot()); + fadcChannelSet.add(cds.getFADCChannel()); + splitterSet.add(cds.getSplitterNumber()); + hvGroupSet.add(cds.getHighVoltageGroup()); + joutSet.add(cds.getJout()); + channelSet.add(cds.getChannel()); + gainSet.add(cds.getGain()); + } + + // Dump all of the set data into a list for each field. + Integer[] xIndex = xIndexSet.toArray(new Integer[xIndexSet.size()]); + Integer[] yIndex = yIndexSet.toArray(new Integer[yIndexSet.size()]); + Integer[] apd = apdSet.toArray(new Integer[apdSet.size()]); + Preamplifier[] preamp = preampSet.toArray(new Preamplifier[preampSet.size()]); + Integer[] ledChannel = ledChannelSet.toArray(new Integer[ledChannelSet.size()]); + Double[] ledDriver = ledDriverSet.toArray(new Double[ledDriverSet.size()]); + Integer[] fadcSlot = fadcSlotSet.toArray(new Integer[fadcSlotSet.size()]); + Integer[] fadcChannel = fadcChannelSet.toArray(new Integer[fadcChannelSet.size()]); + Integer[] splitter = splitterSet.toArray(new Integer[splitterSet.size()]); + Integer[] hvGroup = hvGroupSet.toArray(new Integer[hvGroupSet.size()]); + Integer[] jout = joutSet.toArray(new Integer[joutSet.size()]); + Integer[] channel = channelSet.toArray(new Integer[channelSet.size()]); + Integer[] gain = gainSet.toArray(new Integer[gainSet.size()]); + + // Generate the list of entries for the motherboard spinner. + //String[] motherboard = { "Top", "Bottom", "Left", "Right", "Top-Left", "Top-Right", + // "Bottom-Left", "Bottom-Right" }; + + // Sort all the lists in their natural order. + Arrays.sort(xIndex); + Arrays.sort(yIndex); + Arrays.sort(apd); + Arrays.sort(preamp); + Arrays.sort(ledChannel); + Arrays.sort(ledDriver); + Arrays.sort(fadcSlot); + Arrays.sort(fadcChannel); + Arrays.sort(splitter); + Arrays.sort(hvGroup); + Arrays.sort(jout); + //Arrays.sort(motherboard); + Arrays.sort(channel); + Arrays.sort(gain); + + // Instantiate each of the field combo boxes. + comboField = new JComboBox[INDICES]; + comboField[INDEX_X] = new JComboBox<Integer>(xIndex); + comboField[INDEX_Y] = new JComboBox<Integer>(yIndex); + comboField[INDEX_APD] = new JComboBox<Integer>(apd); + comboField[INDEX_PREAMP] = new JComboBox<Preamplifier>(preamp); + comboField[INDEX_LED_CHANNEL] = new JComboBox<Integer>(ledChannel); + comboField[INDEX_LED_DRIVER] = new JComboBox<Double>(ledDriver); + comboField[INDEX_FADC_SLOT] = new JComboBox<Integer>(fadcSlot); + comboField[INDEX_FADC_CHANNEL] = new JComboBox<Integer>(fadcChannel); + comboField[INDEX_SPLITTER] = new JComboBox<Integer>(splitter); + comboField[INDEX_HV_GROUP] = new JComboBox<Integer>(hvGroup); + comboField[INDEX_JOUT] = new JComboBox<Integer>(jout); + comboField[INDEX_MOTHERBOARD] = new JComboBox<Position>(Position.values()); + comboField[INDEX_CHANNEL] = new JComboBox<Integer>(channel); + comboField[INDEX_GAIN] = new JComboBox<Integer>(gain); + + // Set the properties of the combo boxes. + for(JComboBox<?> combo : comboField) { + combo.setEnabled(false); + combo.setEditable(false); + add(combo); + } + + // Instantiate and set the properties of the apply button. + buttonApply = new JButton("Apply Filter"); + buttonApply.setToolTipText("Highlight all crystals that match the selected filter."); + buttonApply.setEnabled(false); + add(buttonApply); + buttonApply.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + // Apply the active filter. + applyFilter(); + + // Enable the remove filter button. + buttonRemove.setEnabled(true); + } + }); + + // Instantiate and set the properties of the remove button. + buttonRemove = new JButton("Remove Filter"); + buttonRemove.setToolTipText("Clear all filter-related crystal highlighting."); + buttonRemove.setEnabled(false); + add(buttonRemove); + buttonRemove.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + // Remove the list of filtered crystals. + removeFilter(); + + // Disable the remove filter button. + buttonRemove.setEnabled(false); + } + }); + + // Instantiate and set the properties of the remove button. + buttonClear = new JButton("Clear Filters"); + buttonClear.setToolTipText("Reset all filters to the default and disable them."); + add(buttonClear); + buttonClear.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + // Remove the list of filtered crystals. + removeFilter(); + + // Disable all of the filter check boxes. + for(JCheckBox check : checkActive) { + check.setSelected(false); + } + + // Set the selected filter value for each filter to + // the original value. + for(JComboBox<?> combo : comboField) { + combo.setSelectedIndex(0); + } + } + }); + + // Instantiate the filter status text area. + textStatus = new JTextArea(); + textStatus.setEditable(false); + textStatus.setText("Active Filters: None"); + textStatus.setFont(new Font(Font.MONOSPACED, getFont().getStyle(), getFont().getSize())); + JScrollPane textScroller = new JScrollPane(textStatus); + add(textScroller); + + // Determine if the check boxes or the spinners have a larger + // preferred height. + boolean checkLarger = true; + if(checkActive[INDEX_X].getPreferredSize().height < comboField[INDEX_X].getPreferredSize().height) { + checkLarger = false; + } + + // Place the filter components onto the layout. + for(int index = 0; index < INDICES; index++) { + // The first component needs to use the top of the panel + // for its upper border. + if(index == 0) { + layout.putConstraint(SpringLayout.NORTH, checkActive[index], vexternal, SpringLayout.NORTH, this); + layout.putConstraint(SpringLayout.NORTH, comboField[index], vexternal, SpringLayout.NORTH, this); + } + // Subsequent panels are locked to the previous entry's + // lower border. + else { + if(checkLarger) { + layout.putConstraint(SpringLayout.NORTH, checkActive[index], vinternal, SpringLayout.SOUTH, checkActive[index - 1]); + layout.putConstraint(SpringLayout.NORTH, comboField[index], vinternal, SpringLayout.SOUTH, checkActive[index - 1]); + } + else { + layout.putConstraint(SpringLayout.NORTH, checkActive[index], vinternal, SpringLayout.SOUTH, comboField[index - 1]); + layout.putConstraint(SpringLayout.NORTH, comboField[index], vinternal, SpringLayout.SOUTH, comboField[index - 1]); + } + } + + // All check boxes have left borders locked to the left side + // of the panel. All spinners have left borders locked to + // the right border of the widest check box. Finally, all of + // the spinners have right borders locked the right side of + // the panel. + layout.putConstraint(SpringLayout.WEST, checkActive[index], hexternal, SpringLayout.WEST, this); + layout.putConstraint(SpringLayout.WEST, comboField[index], hinternal, SpringLayout.EAST, largestCheck); + layout.putConstraint(SpringLayout.EAST, comboField[index], -hexternal, SpringLayout.EAST, this); + } + + // Place the status text area. + JComponent previous = comboField[INDICES - 1]; + if(checkLarger) { previous = checkActive[INDICES - 1]; } + layout.putConstraint(SpringLayout.NORTH, textScroller, vexternal, SpringLayout.SOUTH, previous); + layout.putConstraint(SpringLayout.WEST, textScroller, hexternal, SpringLayout.WEST, this); + layout.putConstraint(SpringLayout.EAST, textScroller, -hexternal, SpringLayout.EAST, this); + layout.putConstraint(SpringLayout.SOUTH, textScroller, -hexternal, SpringLayout.NORTH, buttonApply); + + // Place the command buttons onto the layout. + layout.putConstraint(SpringLayout.SOUTH, buttonApply, -vexternal, SpringLayout.SOUTH, this); + layout.putConstraint(SpringLayout.WEST, buttonApply, hexternal, SpringLayout.WEST, this); + layout.putConstraint(SpringLayout.SOUTH, buttonRemove, -vexternal, SpringLayout.SOUTH, this); + layout.putConstraint(SpringLayout.WEST, buttonRemove, hinternal, SpringLayout.EAST, buttonApply); + layout.putConstraint(SpringLayout.SOUTH, buttonClear, -vexternal, SpringLayout.SOUTH, this); + layout.putConstraint(SpringLayout.WEST, buttonClear, hinternal, SpringLayout.EAST, buttonRemove); + } + + /** + * Adds an <code>ActionListener</code> object to this component. + * @param listener - The listener to add. + */ + public void addActionListener(ActionListener listener) { + if(listener != null) { alList.add(listener); } + } + + /** + * Gets the list of <code>ActionListener</code> objects attached to + * this component. + * @return Returns the listeners as an array of <code>ActionListener + * </code> objects. + */ + public ActionListener[] getActionListeners() { + return alList.toArray(new ActionListener[alList.size()]); + } + + /** + * Gets the list of crystals that pass the applied filter. + * @return Returns a <code>List</code> collection of <code>Point + * </code> objects representing the LCSim coordinates of every + * crystal that passes the applied filter. If no filter is active, + * returns <code>null</code>. + */ + public List<Point> getFilteredCrystals() { return filteredList; } + + @Override + public Dimension getPreferredSize() { + // If there is a user defined preferred size, use that. + if(preferredSize != null) { return preferredSize; } + + // Track the minimum size needed to display the panel. + int preferredHeight = 4 * vexternal; + + // Determine if the combo boxes or check boxes are larger. + int checkHeight = checkActive[INDEX_X].getPreferredSize().height; + int comboHeight = comboField[INDEX_X].getPreferredSize().height; + boolean checkLarger = checkHeight > comboHeight; + int compHeight = checkLarger ? checkHeight : comboHeight; + + // Get the preferred height of the filters. + preferredHeight += (INDICES - 1) * (vinternal + compHeight); + + // Add the button preferred height. + preferredHeight += buttonApply.getPreferredSize().height; + + // Add 50 for the text area. + preferredHeight += 150; + + // The preferred width is the width needed by the buttons. + int preferredWidth = hexternal + hexternal + hinternal + hinternal; + preferredWidth += buttonApply.getPreferredSize().width; + preferredWidth += buttonRemove.getPreferredSize().width; + preferredWidth += buttonClear.getPreferredSize().width; + + // Return the result. + return new Dimension(preferredWidth, preferredHeight); + } + + /** + * Indicates whether or not a filter is currently applied. + * @return Returns <code>true</code> if there is an active filter + * and <code>false</code> if there is not. + */ + public boolean isActive() { return filteredList != null; } + + /** + * Removes the indicated <code>ActionListener</code> object from + * this component if it exists. + * @param listener - The listener to remove. + */ + public void removeActionListener(ActionListener listener) { + alList.remove(listener); + } + + @Override + public void setPreferredSize(Dimension preferredSize) { + this.preferredSize = preferredSize; + } + + /** + * Applies the filter defined by the currently selected filter + * options. If no filter options are selected, this does nothing. + */ + private void applyFilter() { + // Make sure that at least one filter option is selected. + boolean isEnabled = false; + activeLoop: + for(JCheckBox check : checkActive) { + if(check.isEnabled()) { + isEnabled = true; + break activeLoop; + } + } + if(!isEnabled) { return; } + + // Clear the filter parameters. + cdsCompare.clearFilters(); + + // Store the textual form of the active filters. + StringBuffer filterBuffer = new StringBuffer("Active Filters:\n"); + + // Set the filter parameters. + if(checkActive[INDEX_X].isSelected()) { + int val = (Integer) comboField[INDEX_X].getSelectedItem(); + cdsCompare.addXIndexFilter(val); + filterBuffer.append(String.format(" %-20s :: %d%n", "x-Index", val)); + } + if(checkActive[INDEX_Y].isSelected()) { + int val = (Integer) comboField[INDEX_Y].getSelectedItem(); + cdsCompare.addYIndexFilter(val); + filterBuffer.append(String.format(" %-20s :: %d%n", "y-Index", val)); + } + if(checkActive[INDEX_APD].isSelected()) { + int val = (Integer) comboField[INDEX_APD].getSelectedItem(); + cdsCompare.addAPDFilter(val); + filterBuffer.append(String.format(" %-20s :: %d%n", "APD Number", val)); + } + if(checkActive[INDEX_PREAMP].isSelected()) { + Preamplifier val = (Preamplifier) comboField[INDEX_PREAMP].getSelectedItem(); + cdsCompare.addPreampFilter((Preamplifier) comboField[INDEX_PREAMP].getSelectedItem()); + filterBuffer.append(String.format(" %-20s :: %s%n", "APD Number", val.toString())); + } + if(checkActive[INDEX_LED_CHANNEL].isSelected()) { + int val = (Integer) comboField[INDEX_LED_CHANNEL].getSelectedItem(); + cdsCompare.addLEDChannelFilter(val); + filterBuffer.append(String.format(" %-20s :: %d%n", "LED Channel Number", val)); + } + if(checkActive[INDEX_LED_DRIVER].isSelected()) { + double val = (Double) comboField[INDEX_LED_DRIVER].getSelectedItem(); + cdsCompare.addLEDDriverFilter(val); + filterBuffer.append(String.format(" %-20s :: %.1f%n", "LED Driver", val)); + } + if(checkActive[INDEX_FADC_SLOT].isSelected()) { + int val = (Integer) comboField[INDEX_FADC_SLOT].getSelectedItem(); + cdsCompare.addFADCSlotFilter(val); + filterBuffer.append(String.format(" %-20s :: %d%n", "FADC Slot Number", val)); + } + if(checkActive[INDEX_FADC_CHANNEL].isSelected()) { + int val = (Integer) comboField[INDEX_FADC_CHANNEL].getSelectedItem(); + cdsCompare.addFADCChannelFilter(val); + filterBuffer.append(String.format(" %-20s :: %d%n", "FADC Channel Number", val)); + } + if(checkActive[INDEX_SPLITTER].isSelected()) { + int val = (Integer) comboField[INDEX_SPLITTER].getSelectedItem(); + cdsCompare.addSplitterFilter(val); + filterBuffer.append(String.format(" %-20s :: %d%n", "Splitter Number", val)); + } + if(checkActive[INDEX_HV_GROUP].isSelected()) { + int val = (Integer) comboField[INDEX_HV_GROUP].getSelectedItem(); + cdsCompare.addHVGroupFilter(val); + filterBuffer.append(String.format(" %-20s :: %d%n", "High Voltage Group", val)); + } + if(checkActive[INDEX_JOUT].isSelected()) { + int val = (Integer) comboField[INDEX_JOUT].getSelectedItem(); + cdsCompare.addJoutFilter(val); + filterBuffer.append(String.format(" %-20s :: %d%n", "Jout", val)); + } + if(checkActive[INDEX_MOTHERBOARD].isSelected()) { + Position val = (Position) comboField[INDEX_MOTHERBOARD].getSelectedItem(); + cdsCompare.addMotherboardFilter((Position) comboField[INDEX_MOTHERBOARD].getSelectedItem()); + filterBuffer.append(String.format(" %-20s :: %s%n", "Motherboard Position", val.toString())); + } + if(checkActive[INDEX_CHANNEL].isSelected()) { + int val = (Integer) comboField[INDEX_CHANNEL].getSelectedItem(); + cdsCompare.addChannelFilter(val); + filterBuffer.append(String.format(" %-20s :: %d%n", "Channel Number", val)); + } + if(checkActive[INDEX_GAIN].isSelected()) { + int val = (Integer) comboField[INDEX_GAIN].getSelectedItem(); + cdsCompare.addGainFilter(val); + filterBuffer.append(String.format(" %-20s :: %d%n", "Gain", val)); + } + + // Update the status pane. + textStatus.setText(filterBuffer.toString()); + + // Populate the filtered crystals list. + filteredList = new ArrayList<Point>(); + for(CrystalDataSet cds : manager) { + if(cdsCompare.passesFilter(cds)) { filteredList.add(cds.getCrystalIndex()); } + } + + // Throw an event to indicate that a filter was applied. + ActionEvent event = new ActionEvent(this, EVENT_FILTER_APPLIED, "Filter Applied"); + for(ActionListener lst : alList) { + lst.actionPerformed(event); + } + } + + /** + * Removes any active filters. + */ + private void removeFilter() { + // Make sure that there is an active filter to remove. + if(filteredList == null) { return; } + + // Clear the filter list. + filteredList = null; + + // Update the status panel text. + textStatus.setText("Active Filters: None"); + + // Throw an event to indicate that a filter was removed. + ActionEvent event = new ActionEvent(this, EVENT_FILTER_REMOVED, "Filter Removed"); + for(ActionListener lst : alList) { + lst.actionPerformed(event); + } + } + + /** + * Enumerable <code>Position</code> specifies a location on the + * calorimeter. + * + * @author Kyle McCarty + */ + private enum Position { + TOP(true, null), BOTTOM(false, null), LEFT(null, true), RIGHT(null, false), + TOP_LEFT(true, true), TOP_RIGHT(true, false), BOTTOM_LEFT(false, true), BOTTOM_RIGHT(false, false); + + // Internal variables. + private String name = null; + private final Boolean isTop; + private final Boolean isLeft; + + /** + * Instantiates a new position. + * @param isTop - <code>true</code> if the position is on the + * top and <code>false</code> if it is on the bottom. <code> + * null</code> indicates that it does not differentiate between + * this axis. + * @param isLeft - <code>true</code> if the position is on the + * left and <code>false</code> if it is on the right. <code> + * null</code> indicates that it does not differentiate between + * this axis. + */ + private Position(Boolean isTop, Boolean isLeft) { + this.isTop = isTop; + this.isLeft = isLeft; + } + + /** + * Checks whether a given motherboard position is consistent + * with the position enumerable. + * @param mb - The motherboard to check. + * @return Returns <code>true</code> if the motherboard position + * is consistent and <code>false</code> otherwise. + */ + public boolean matchesPosition(Motherboard mb) { + // If this position needs to check top or bottom status, + // do so. Return false if it fails. + if(isTop != null && isTop != mb.isTop()) { + return false; + } + + // If this position needs to check left or right status, + // do so. Return false if it fails. + if(isLeft != null && isLeft != mb.isLeft()) { + return false; + } + + // If it passes all checks, return true. + return true; + } + + @Override + public String toString() { + // If the name has already been defined, return it. + if(name != null) { return name; } + + // Otherwise, the name must be parsed from the system name. + // Create a string buffer to contain the parsed characters. + StringBuffer nameBuffer = new StringBuffer(); + + // Iterate over the characters in the name and process them. + boolean nextUpperCase = true; + for(char c : name().toCharArray()) { + if(c == '_') { + nameBuffer.append(' '); + nextUpperCase = true; + } + else if(nextUpperCase) { + nameBuffer.append(c); + nextUpperCase = false; + } + else { nameBuffer.append(Character.toLowerCase(c)); } + } + + // Store the result. + name = nameBuffer.toString(); + + // Return the result. + return name; + } + } + + /** + * Class <code>CheckListener</code> is an implementation of <code> + * ItemListener</code> that is linked to a specific component index + * and handles the enabling and disabling of filters. + * + * @author Kyle McCarty + * @see ItemListener + */ + private class CheckListener implements ItemListener { + // Store the index of the components associated with the listener. + private final int index; + + /** + * Instantiates a new <code>CheckListener</code> that is linked + * to the specified index. + * @param componentIndex - The index of the component. + */ + public CheckListener(int componentIndex) { index = componentIndex; } + + @Override + public void itemStateChanged(ItemEvent e) { + // Set the component properties according the selection + // status of the check box. + if(checkActive[index].isSelected()) { + checkActive[index].setForeground(COLOR_CHECK_FONT_ACTIVE); + comboField[index].setEnabled(true); + } + else { + checkActive[index].setForeground(COLOR_CHECK_FONT_INACTIVE); + comboField[index].setEnabled(false); + } + + // If any of the filter check boxes are active, the active + // filter button should be clickable. + boolean active = false; + applyCheckLoop: + for(JCheckBox check : checkActive) { + if(check.isSelected()) { + active = true; + break applyCheckLoop; + } + } + + // Set the apply button to the appropriate setting. + buttonApply.setEnabled(active); + } + } + + /** + * Class <code>CDSComparator</code> allows a set of filters for + * each property in a <code>CrystalDataSet</code> to be defined. + * It then will check to see if a given <code>CrystalDataSet</code> + * object matches the <code>CDSComparator</code> properties. + * + * @author Kyle McCarty + */ + private class CDSComparator { + // Filters. + private Integer filterX = null; + private Integer filterY = null; + private Integer filterAPD = null; + private Preamplifier filterPreamp = null; + private Integer filterLEDChannel = null; + private Double filterLEDDriver = null; + private Integer filterFADCSlot = null; + private Integer filterFADCChannel = null; + private Integer filterSplitter = null; + private Integer filterHVGroup = null; + private Integer filterJout = null; + private Position motherboard = null; + private Integer filterChannel = null; + private Integer filterGain = null; + + /** + * Enables and sets the value of the x-index filter. + * @param xIndex - The filter value. + */ + public void addXIndexFilter(int xIndex) { filterX = new Integer(xIndex); } + + /** + * Enables and sets the value of the y-index filter. + * @param yIndex - The filter value. + */ + public void addYIndexFilter(int yIndex) { filterY = new Integer(yIndex); } + + /** + * Enables and sets the value of the APD number filter. + * @param apd - The filter value. + */ + public void addAPDFilter(int apd) { filterAPD = new Integer(apd); } + + /** + * Enables and sets the value of the preamplifier number filter. + * @param preamp - The filter value. + */ + public void addPreampFilter(Preamplifier preamp) { filterPreamp = preamp; } + + /** + * Enables and sets the value of the LED channel number filter. + * @param ledChannel - The filter value. + */ + public void addLEDChannelFilter(int ledChannel) { filterLEDChannel = new Integer(ledChannel); } + + /** + * Enables and sets the value of the LED driver filter. + * @param ledDriver - The filter value. + */ + public void addLEDDriverFilter(double ledDriver) { filterLEDDriver = new Double(ledDriver); } + + /** + * Enables and sets the value of the FADC slot filter. + * @param fadcSlot - The filter value. + */ + public void addFADCSlotFilter(int fadcSlot) { filterFADCSlot = new Integer(fadcSlot); } + + /** + * Enables and sets the value of the FADC channel filter. + * @param fadcChannel - The filter value. + */ + public void addFADCChannelFilter(int fadcChannel) { filterFADCChannel = new Integer(fadcChannel); } + + /** + * Enables and sets the value of the splitter number filter. + * @param splitterNum - The filter value. + */ + public void addSplitterFilter(int splitterNum) { filterSplitter = new Integer(splitterNum); } + + /** + * Enables and sets the value of the high voltage group filter. + * @param hvGroup - The filter value. + */ + public void addHVGroupFilter(int hvGroup) { filterHVGroup = new Integer(hvGroup); } + + /** + * Enables and sets the value of the Jout filter. + * @param jout - The filter value. + */ + public void addJoutFilter(int jout) { filterJout = new Integer(jout); } + + /** + * Enables and sets the value of the motherboard position filter. + * @param mb - The filter value. + */ + public void addMotherboardFilter(Position mb) { motherboard = mb; } + + /** + * Enables and sets the value of the channel number filter. + * @param channelNum - The filter value. + */ + public void addChannelFilter(int channelNum) { filterChannel = new Integer(channelNum); } + + /** + * Enables and sets the value of the gain filter. + * @param gain - The filter value. + */ + public void addGainFilter(int gain) { filterGain = new Integer(gain); } + + /** + * Resets all of the filters to the default disabled state. + */ + public void clearFilters() { + filterX = null; + filterY = null; + filterAPD = null; + filterPreamp = null; + filterLEDChannel = null; + filterLEDDriver = null; + filterFADCSlot = null; + filterFADCChannel = null; + filterSplitter = null; + filterHVGroup = null; + filterJout = null; + motherboard = null; + filterChannel = null; + filterGain = null; + } + + /** + * Checks to see if a given <code>CrystalDataSet</code> object + * matches all of the defined filter parameters. + * @param cds - The data set to check. + * @return Returns <code>true</code> if the <code>CrystalDataSet + * </code> matches the defined filter parameters and <code>false + * </code> otherwise. + */ + public boolean passesFilter(CrystalDataSet cds) { + // Apply the filter for the crystal x-index. + if(filterX != null) { + if(cds.getCrystalXIndex() != filterX) { return false; } + } + + // Apply the filter for the crystal y-index. + if(filterY != null) { + if(cds.getCrystalYIndex() != filterY) { return false; } + } + + // Apply the filter for the crystal APD number. + if(filterAPD != null) { + if(cds.getAPDNumber() != filterAPD) { return false; } + } + + // Apply the filter for the associated preamplifier. + if(filterPreamp != null) { + if(cds.getPreamplifierNumber() != filterPreamp) { return false; } + } + + // Apply the filter for the crystal LED channel number. + if(filterLEDChannel != null) { + if(cds.getLEDChannel() != filterLEDChannel) { return false; } + } + + // Apply the filter for the crystal LED driver. + if(filterLEDDriver != null) { + if(cds.getLEDDriver() != filterLEDDriver) { return false; } + } + + // Apply the filter for the crystal FADC slot. + if(filterFADCSlot != null) { + if(cds.getFADCSlot() != filterFADCSlot) { return false; } + } + + // Apply the filter for the crystal FADC channel. + if(filterFADCChannel != null) { + if(cds.getFADCChannel() != filterFADCChannel) { return false; } + } + + // Apply the filter for the crystal splitter number. + if(filterSplitter != null) { + if(cds.getSplitterNumber() != filterSplitter) { return false; } + } + + // Apply the filter for the crystal high voltage group. + if(filterHVGroup != null) { + if(cds.getHighVoltageGroup() != filterHVGroup) { return false; } + } + + // Apply the filter for the Jout property. + if(filterJout != null) { + if(cds.getJout() != filterJout) { return false; } + } + + // Apply the filter for the motherboard position. + if(motherboard != null) { + if(!motherboard.matchesPosition(cds.getMotherboard())) { return false; } + } + + // Apply the filter for the crystal channel number. + if(filterChannel != null) { + if(cds.getChannel() != filterChannel) { return false; } + } + + // Apply the filter for the crystal gain. + if(filterGain != null) { + if(cds.getGain() != filterGain) { return false; } + } + + // If all filters succeeded, return true. + return true; + } + } +} Modified: java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/DataFileViewer.java ============================================================================= --- java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/DataFileViewer.java (original) +++ java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/DataFileViewer.java Mon Nov 3 11:46:24 2014 @@ -1,7 +1,14 @@ package org.hps.monitoring.ecal.eventdisplay.ui; import java.awt.Point; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; import java.io.IOException; +import java.util.List; + +import javax.swing.JFrame; import org.hps.monitoring.ecal.eventdisplay.io.EventManager; import org.hps.monitoring.ecal.eventdisplay.util.CrystalDataSet; @@ -40,6 +47,10 @@ private static final int FIELD_CHANNEL = 10; private static final int FIELD_GAIN = 11; + // Filter panel components. + private JFrame filterWindow; + private CrystalFilterPanel filterPanel; + /** * Initializes a new <code>DataFileViewer</code> that reads from * the given event manager for event data and the given hardware @@ -61,6 +72,48 @@ for(String fieldName : fieldNames) { addStatusField(fieldName); } + + // Instantiate the crystal filter panel. + filterPanel = new CrystalFilterPanel(ewm); + filterWindow = new JFrame("Event Display Crystal Filter"); + filterWindow.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); + filterWindow.add(filterPanel); + filterWindow.pack(); + filterWindow.setResizable(false); + + // Add an action listener to note when the filter window applies + // a crystal filter. + filterPanel.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + // Clear the panel highlighting. + ecalPanel.clearHighlight(); + + // If the filter panel is active, highlight the crystals + // that passed the filter. + if(filterPanel.isActive()) { + // Get the list of filtered crystals. + List<Point> filterList = filterPanel.getFilteredCrystals(); + + // Highlight each of the filtered crystals. + for(Point crystal : filterList) { + ecalPanel.setCrystalHighlight(toPanelPoint(crystal), java.awt.Color.WHITE); + } + } + } + }); + + // Create a new key listener. + addKeyListener(new KeyAdapter() { + @Override + public void keyReleased(KeyEvent e) { + // Bring up the filter panel when 'f' is pressed. + if(e.getKeyCode() == 70) { + if(!filterWindow.isVisible()) { filterWindow.setVisible(true); } + else { filterWindow.setVisible(false); } + } + } + }); } @Override @@ -90,7 +143,7 @@ setStatusField(fieldNames[FIELD_SPLITTER], "" + cds.getSplitterNumber()); setStatusField(fieldNames[FIELD_HV_GROUP], "" + cds.getHighVoltageGroup()); setStatusField(fieldNames[FIELD_JOUT], "" + cds.getJout()); - setStatusField(fieldNames[FIELD_MB], "" + cds.getMB()); + setStatusField(fieldNames[FIELD_MB], "" + cds.getMotherboard().toString()); setStatusField(fieldNames[FIELD_CHANNEL], "" + cds.getChannel()); setStatusField(fieldNames[FIELD_GAIN], "" + cds.getGain()); } Modified: java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/PDataEventViewer.java ============================================================================= --- java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/PDataEventViewer.java (original) +++ java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/PDataEventViewer.java Mon Nov 3 11:46:24 2014 @@ -89,7 +89,7 @@ setStatusField(fieldNames[FIELD_SPLITTER], "" + cds.getSplitterNumber()); setStatusField(fieldNames[FIELD_HV_GROUP], "" + cds.getHighVoltageGroup()); setStatusField(fieldNames[FIELD_JOUT], "" + cds.getJout()); - setStatusField(fieldNames[FIELD_MB], "" + cds.getMB()); + setStatusField(fieldNames[FIELD_MB], "" + cds.getMotherboard().toString()); setStatusField(fieldNames[FIELD_CHANNEL], "" + cds.getChannel()); setStatusField(fieldNames[FIELD_GAIN], "" + cds.getGain()); } Modified: java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/util/CrystalDataSet.java ============================================================================= --- java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/util/CrystalDataSet.java (original) +++ java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/util/CrystalDataSet.java Mon Nov 3 11:46:24 2014 @@ -9,11 +9,11 @@ * * @author Kyle McCarty */ -public class CrystalDataSet { +public class CrystalDataSet { // Data points. private final Point crystalIndex; private final short apd; - private final PreamplifierNumber preamp; + private final Preamplifier preamp; private final short ledChannel; private final byte[] ledDriver; private final byte fadcSlot; @@ -21,7 +21,7 @@ private final byte splitterNum; private final byte hvGroup; private final byte jout; - private final String mb; + private final Motherboard motherboard; private final short channel; private final int gain; @@ -30,19 +30,20 @@ * @param ix - The crystal's x-index in the LCSimcoordinate system. * @param iy - The crystal's y-index in the LCSimcoordinate system. * @param apd - The number of the APD attached to the crystal. - * @param preamp - The number of the crystal's premaplifier. This + * @param preamp - The number of the crystal's preamplifier. This * may also include a reference wire color. * @param ledChannel - The channel number for the crystal's LED. * @param ledDriver * @param fadcSlot - The FADC slot the crystal occupies. * @param fadcChannel - The channel number through which the crystal * communicates with the FADC. - * @param splitter + * @param splitter - The number of the crystal's splitter. * @param hvGroup - The high voltage group for which the crystal * is a member. - * @param jout - * @param mb - * @param channel + * @param jout - The crystal's signal channel group. + * @param mb - The position of the motherboard associated with the + * crystal. + * @param channel - The channel associated with the crystal. * @param gain - The crystal's gain. */ public CrystalDataSet(int ix, int iy, int apd, String preamp, int ledChannel, @@ -61,7 +62,7 @@ this.jout = (byte) jout; this.channel = (short) channel; this.gain = gain; - this.mb = mb; + this.motherboard = Motherboard.getMotherboard(mb); // Define the LED driver. this.ledDriver = new byte[2]; @@ -78,7 +79,7 @@ int number = Integer.parseInt(num.toString()); String color = null; if(col.length() != 0) { color = col.toString(); } - this.preamp = new PreamplifierNumber(number, color); + this.preamp = new Preamplifier(number, color); } /** @@ -115,7 +116,7 @@ * @return Returns the preamplifier reference as a <code> * PreamplifierNumber</code> object. */ - public PreamplifierNumber getPreamplifierNumber() { return preamp; } + public Preamplifier getPreamplifierNumber() { return preamp; } /** * Gets the crystal's LED channel. @@ -139,6 +140,11 @@ */ public int getFADCChannel() { return fadcChannel; } + /** + * Gets the number of the splitter associated with the crystal. + * @return Returns the splitter number as an <code>int</code> + * primitive. + */ public int getSplitterNumber() { return splitterNum; } /** @@ -148,10 +154,25 @@ */ public int getHighVoltageGroup() { return hvGroup; } + /** + * Gets the signal channel group for the crystal. + * @return Returns the signal channel group as an <code>int</code> + * primitive. + */ public int getJout() { return jout; } - public String getMB() { return mb; } - + /** + * Gets the positional information concerning the motherboard with + * which the crystal is associated. + * @return Returns the <code>Motherboard</code> enumerable attached + * to the crystal. + */ + public Motherboard getMotherboard() { return motherboard; } + + /** + * Gets the channel number associated with the crystal. + * @return Gets the channel number as an <code>int</code> primitive. + */ public int getChannel() { return channel; } /** @@ -161,30 +182,29 @@ public int getGain() { return gain; } /** - * Class <code>PreamplifierNumber</code> represents the number + * Class <code>Preamplifier</code> represents the number * of a crystal's preamplifier. It can also contain a reference * wire color if necessary. * * @author Kyle McCarty */ - public class PreamplifierNumber { + public class Preamplifier implements Comparable<Preamplifier> { private final short number; private final String color; /** - * Initializes a preamplifier number with no reference wire - * color. + * Initializes a preamplifier with no reference wire color. * @param number - The preamplifier's number. */ - public PreamplifierNumber(int number) { this(number, null); } - - /** - * Initializes a preamplifier number with the specified reference + public Preamplifier(int number) { this(number, null); } + + /** + * Initializes a preamplifier with the specified reference * wire color. * @param number - The preamplifier's number. * @param color - The reference wire color. */ - public PreamplifierNumber(int number, String color) { + public Preamplifier(int number, String color) { this.number = (short) number; this.color = color; } @@ -209,5 +229,159 @@ if(color == null) { return "" + number; } else { return number + " (" + color + ")"; } } + + @Override + public int compareTo(Preamplifier preamp) { + // Compare the preamplifier numbers. + int numCompare = Short.compare(number, preamp.number); + + // If they are different, return this value. + if(numCompare != 0) { return numCompare; } + + // The color string must now be compared. If both are null, + // then they are the same. + if(color == null && preamp.color == null) { return 0; } + + // If one is null and the other is not, the null one is + // always ordered first. + if(color == null && preamp.color != null) { return -1; } + else if(color != null && preamp.color == null) { return 1; } + + // If neither color is null, compare them traditionally. + return color.compareTo(preamp.color); + } + } + + /** + * Enumerable <code>Motherboard</code> contains convenience methods + * for defining the motherboard location for a particular crystal. + * + * @author Kyle McCarty + */ + public enum Motherboard { + /** The motherboard on the upper, left-hand side. */ + TOP_LEFT(true, true), + /** The motherboard on the upper, right-hand side. */ + TOP_RIGHT(true, false), + /** The motherboard on the lower, left-hand side. */ + BOTTOM_LEFT(false, true), + /** The motherboard on the lower, right-hand side. */ + BOTTOM_RIGHT(false, false); + + private final boolean isTop; + private final boolean isLeft; + + /** + * Instantiates a new motherboard enumerable. + * @param isTop - Indicates whether the motherboard is on the + * upper side of the detector. + * @param isLeft - Indicates whether the motherboard is on the + * left-hand side of the detector. + */ + private Motherboard(boolean isTop, boolean isLeft) { + this.isTop = isTop; + this.isLeft = isLeft; + } + + /** + * Indicates whether the motherboard is on the top of the + * detector or not. + * @return Returns <code>true</code> if the motherboard is on + * the upper half of the detector and <code>false</code> if it + * is not. + */ + public boolean isTop() { return isTop; } + + /** + * Indicates whether the motherboard is on the bottom of the + * detector or not. + * @return Returns <code>true</code> if the motherboard is on + * the lower half of the detector and <code>false</code> if it + * is not. + */ + public boolean isBottom() { return !isTop; } + + /** + * Indicates whether the motherboard is on the left-hand side + * of the detector or not. + * @return Returns <code>true</code> if the motherboard is on + * the left-hand side of the detector and <code>false</code> + * if it is not. + */ + public boolean isLeft() { return isLeft; } + + /** + * Indicates whether the motherboard is on the right-hand side + * of the detector or not. + * @return Returns <code>true</code> if the motherboard is on + * the right-hand side of the detector and <code>false</code> + * if it is not. + */ + public boolean isRight() { return !isLeft; } + + /** + * Gets the <code>Motherboard</code> enumerable associated + * with the given textual abbreviation. Valid arguments include + * "LT", "RT", "LB", and "RB". + * @param abbreviation - The textual abbreviation for the + * location of the motherboard. + * @return Returns the appropriate <code>Motherboard</code> + * enumerable if given a valid abbreviation and <code>null + * </code> otherwise. + */ + public static final Motherboard getMotherboard(String abbreviation) { + // Abbreviations must be 2 characters in length. + if(abbreviation.length() != 2) { return null; } + + // The first character must be either 'R' or 'L'. + boolean isLeft; + if(abbreviation.charAt(0) == 'L') { isLeft = true; } + else if(abbreviation.charAt(0) == 'R') { isLeft = false; } + else { return null; } + + // The second character must be either 'T' or 'B'. + boolean isTop; + if(abbreviation.charAt(1) == 'T') { isTop = true; } + else if(abbreviation.charAt(1) == 'B') { isTop = false; } + else { return null; } + + // Return the appropriate motherboard enumerable. + return getMotherboard(isTop, isLeft); + } + + /** + * Gets the <code>Motherboard</code> enumerable associated + * with the given position. + * @param isTop - <code>true</code> indicates the motherboard + * on the top half of the detector and <code>false</code> the + * motherboard on the bottom half. + * @param isLeft - <code>true</code> indicates the motherboard + * on the left-hand side of the detector and <code>false</code> + * the motherboard on the right-hand side. + * @return Returns the appropriate <code>Motherboard</code> + * enumerable. + */ + public static final Motherboard getMotherboard(boolean isTop, boolean isLeft) { + if(isTop) { + if(isLeft) { return TOP_LEFT; } + else { return TOP_RIGHT; } + } + else { + if(isLeft) { return BOTTOM_LEFT; } + else { return BOTTOM_RIGHT; } + } + } + + @Override + public String toString() { + if(isTop) { + if(isLeft) { return "LT"; } + else { return "RT"; } + } + else { + if(isLeft) { return "LB"; } + else { return "RB"; } + } + } } } Modified: java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/util/EcalWiringManager.java ============================================================================= --- java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/util/EcalWiringManager.java (original) +++ java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/util/EcalWiringManager.java Mon Nov 3 11:46:24 2014 @@ -4,7 +4,11 @@ import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; +import java.util.ArrayList; +import java.util.Comparator; import java.util.HashMap; +import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Scanner; @@ -17,7 +21,7 @@ * * @author Kyle McCarty */ -public class EcalWiringManager { +public class EcalWiringManager implements Iterable<CrystalDataSet> { // Delimiter class statics. public static final int SPACE_DELIMITED = 1; public static final int TAB_DELIMITED = 2; @@ -109,4 +113,35 @@ * if the crystal index is invalid. */ public CrystalDataSet getCrystalData(Point crystalIndex) { return crystalMap.get(crystalIndex); } + + /** + * Generates a list of all the crystals that match the conditions + * set in the argument <code>Comparator</code> object. + * @param conditions - An object defining the conditions that denote + * a "matched" crystal. + * @return Returns <code>Point</code> objects that contain the + * LCSim coordinates of all the crystals that produce a result of + * <code>true</code> with the method <code>comparator.equals(crystal) + * </code>. + */ + public List<Point> getFilteredCrystalList(Comparator<CrystalDataSet> conditions) { + // Create a list of crystal indices that match the conditions. + List<Point> crystalList = new ArrayList<Point>(); + + // Iterate over the crystal data set entries. + for(CrystalDataSet cds : this) { + // Check if the crystal data set meets the given conditions. + if(conditions.equals(cds)) { + crystalList.add(cds.getCrystalIndex()); + } + } + + // Return the resultant list. + return crystalList; + } + + @Override + public Iterator<CrystalDataSet> iterator() { + return new MapValueIterator<CrystalDataSet>(crystalMap); + } } Added: java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/util/MapValueIterator.java ============================================================================= --- java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/util/MapValueIterator.java (added) +++ java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/util/MapValueIterator.java Mon Nov 3 11:46:24 2014 @@ -0,0 +1,39 @@ +package org.hps.monitoring.ecal.eventdisplay.util; + +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +/** + * Class <code>MapValueIterator</code> creates an iterator for the + * values stored in a map. + * + * @author Kyle McCarty + * @param E The object type of the map's values. + */ +public class MapValueIterator<E> implements Iterator<E> { + private Iterator<? extends Entry<?, E>> baseIterator; + + /** + * Generates a new <code>MapValueIterator</code> from a given <code> + * Map</code> with the appropriate parameterizations. + */ + public MapValueIterator(Map<?, E> sourceMap) { baseIterator = sourceMap.entrySet().iterator(); } + + @Override + public boolean hasNext() { return baseIterator.hasNext(); } + + @Override + public E next() { + // Get the next entry in the base iterator. + Entry<?, E> next = baseIterator.next(); + + // Return the value of the entry. + return next.getValue(); + } + + @Override + public void remove() throws UnsupportedOperationException { + throw new UnsupportedOperationException("Operation \"remove\" is not supported for MapValueIterator."); + } +}