package gui.views; import gui.main.EventController; import gui.settings.Settings; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.io.IOException; import java.util.HashSet; import java.util.Observable; import javax.imageio.ImageIO; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JOptionPane; import javax.swing.JTable; import controller.DataHub; import controller.ElementData; import controller.Feature; import controller.Group; import controller.SelectionController; import controller.SubspaceController; import db.DatabaseAccessException; /** * DetailView offers the possibility to hide and show the selection details. It also calculates the data for the * selection and initializes all other panels used to visualize the details. */ public class DetailView extends ViewPanel { private static final long serialVersionUID = -2765739962910385615L; /** * The detailPanel that visualizes the actual data. */ private DetailPanel detailPanel; /** * The hide/show button. */ private final JButton showButton; /** * Arrow icons for the showButton. */ private ImageIcon arrowRight; private ImageIcon arrowLeft; /** * Array holding the calculated feature data. */ private float[][] featureData; /** * Array holding the calculated subspace data. */ private float[][] subspaceData; /** * Array holding the raw data from dataHub. */ private ElementData[] elementData; /** * List of the features. */ private Feature[] featureFeatures; /** * List of the outlier features. */ private Feature[] outlierFeatures; /** * Number of currently selected objects. */ private int selectedCount; /** * List of involved groups. */ private String[] groups; /** * List of the columnNames. */ private String[] detailTableHeaderStrings; /** * Calculate the minimum. */ private boolean calcMin = true; /** * Calculate maximum. */ private boolean calcMax = true; /** * Calculate the average. */ private boolean calcAverage = true; /** * Calculate the variance. */ private boolean calcVariance = false; /** * Calculate the standard deviation. */ private boolean calcStandardDeviation = false; /** * Calculate the median. */ private boolean calcMedian = false; /** * Minimum column. */ private int minColumn; /** * Maximum column. */ private int maxColumn; /** * Average column. */ private int averageColumn; /** * Variance column. */ private int varianceColumn; /** * Standard deviation column. */ private int standardDeviationColumn; /** * Median column. */ private int medianColumn; /** * Number of shown columns. */ private int columnCount; /** * Error flag. */ private boolean errorOccured; /** * Constructs a new detail view. * * @param dataHub * instance of the DataHub. * @param selectionController * instance of the SelectionController. * @param subspaceController * instance of the SubspaceController. */ public DetailView(DataHub dataHub, SelectionController selectionController, SubspaceController subspaceController) { super(dataHub, selectionController, subspaceController); //Add shortcut EventController.getInstance().registerKeyTarget(this); EventController.getInstance().setAction(new ShowHideAction(), "eventShowHideDetailView"); this.errorOccured = false; this.setLayout(new BorderLayout()); this.setBorder(BorderFactory.createMatteBorder(0, 1, 0, 0, Color.GRAY)); try { arrowRight = new ImageIcon(ImageIO.read(this.getClass().getResourceAsStream("/arrow_right.png"))); arrowLeft = new ImageIcon(ImageIO.read(this.getClass().getResourceAsStream("/arrow_left.png"))); } catch (IOException e) { // no icons } update(null, null); detailPanel.setVisible(false); this.showButton = new JButton(new ShowHideAction()); this.showButton.setIcon(arrowLeft); this.showButton.setMinimumSize(new Dimension(20, 30)); this.showButton.setMaximumSize(new Dimension(20, 30)); this.showButton.setPreferredSize(new Dimension(20, 30)); this.add(showButton, BorderLayout.EAST); } private class ShowHideAction extends AbstractAction { private static final long serialVersionUID = -8846800701591409867L; @Override public void actionPerformed(ActionEvent e) { if (detailPanel.isVisible()) { detailPanel.setVisible(false); showButton.setIcon(arrowLeft); } else { detailPanel.setVisible(true); showButton.setIcon(arrowRight); } } } /** * Updates the detailPanel if something has changed and visualizes it. */ private void updateDetailPanel() { validateDetailColumnInfo(); featureData = calcData(featureFeatures); subspaceData = calcData(outlierFeatures); groups = calcGroupNames(); selectedCount = selectionController.getSelectedCount(); elementData = null; DetailTableModel detm1 = new DetailTableModel(featureFeatures, featureData, detailTableHeaderStrings, selectionController.isSomethingSelected()); DetailTableModel detm2 = new DetailTableModel(outlierFeatures, subspaceData, detailTableHeaderStrings, selectionController.isSomethingSelected()); JTable featureTable = new JTable(detm1); JTable subspaceTable = new JTable(detm2); if (detailPanel != null) { this.remove(detailPanel); } detailPanel = new DetailPanel(this, featureTable, subspaceTable, selectedCount, groups); this.add(detailPanel, BorderLayout.CENTER); this.validate(); } /** * Calculates which groups are involved. */ private String[] calcGroupNames() { HashSet<Group> result = new HashSet<Group>(); for (ElementData element : elementData) { if (selectionController.isSelected(element.getId())) { for (Group group : element.getGroups()) { result.add(group); } } } String[] stringArray = new String[result.size()]; Object[] resultArray = result.toArray(); for (int i = 0; i < result.size(); i++) { stringArray[i] = resultArray[i].toString(); } return stringArray; } @Override public void update(Observable arg0, Object arg1) { if (detailPanel == null || detailPanel.isVisible()) { this.errorOccured = false; getData(); getFeatures(); if (this.errorOccured) { this.showErrorMessage(); } else { this.updateDetailPanel(); } } } @Override public String getName() { return Settings.getInstance().getResourceBundle().getString("detailName"); } /** * Calculates the data. */ private float[][] calcData(Feature[] features) { float[][] calculatedData = new float[features.length][columnCount]; float min; float max; float average; int featureCount = 0; float value = 0; float[] featureValues = new float[selectionController.getSelectedCount()]; int selectedCount = 0; if (selectionController.isSomethingSelected()) { for (Feature feature : features) { min = Float.MAX_VALUE; max = -Float.MAX_VALUE; average = 0; selectedCount = 0; for (ElementData element : elementData) { if (selectionController.isSelected(element.getId())) { value = element.getValue(feature); featureValues[selectedCount] = value; selectedCount++; // test min if (calcMin && value < min) { min = value; } // test max if (calcMax && value > max) { max = value; } // calc average if (calcAverage || calcVariance || calcStandardDeviation) { average += value; } } } if (calcMin) { calculatedData[featureCount][minColumn] = min; } if (calcMax) { calculatedData[featureCount][maxColumn] = max; } if (calcAverage || calcVariance || calcStandardDeviation) { average /= selectionController.getSelectedCount(); } float variance = 0; float tmpValue = 0; for (ElementData element : elementData) { if (selectionController.isSelected(element.getId())) { tmpValue = element.getValue(feature) - average; variance += tmpValue * tmpValue; } } if (calcAverage) { calculatedData[featureCount][averageColumn] = average; } if (calcVariance) { calculatedData[featureCount][varianceColumn] = variance / (selectionController.getSelectedCount()); } if (calcStandardDeviation) { calculatedData[featureCount][standardDeviationColumn] = (float) Math.sqrt(variance / (selectionController.getSelectedCount())); } float median; if (calcMedian) { java.util.Arrays.sort(featureValues); if ((featureValues.length % 2) == 0) { median = (featureValues[(featureValues.length / 2) - 1] + featureValues[(featureValues.length / 2)]) / 2; } else { median = featureValues[(featureValues.length / 2)]; } calculatedData[featureCount][medianColumn] = median; } featureCount++; } } return calculatedData; } /** * Fetches the new data. */ private void getData() { try { elementData = dataHub.getData(); } catch (DatabaseAccessException e) { this.errorOccured = true; } } /** * Fetches the features and splits them in normal features and outlier features. */ private void getFeatures() { Feature[] features = null; int nrFeatures = 0; int nrOutlier = 0; try { features = subspaceController.getActiveSubspace().getFeatures(); } catch (DatabaseAccessException e) { this.errorOccured = false; } for (Feature x : features) { if (x.isOutlier()) { nrOutlier++; } else { nrFeatures++; } } featureFeatures = new Feature[nrFeatures]; outlierFeatures = new Feature[nrOutlier]; nrFeatures = 0; nrOutlier = 0; for (Feature x : features) { if (x.isOutlier()) { outlierFeatures[nrOutlier] = x; nrOutlier++; } else { featureFeatures[nrFeatures] = x; nrFeatures++; } } } /** * Evaluates which data is calculated an sets the header titles. */ private void validateDetailColumnInfo() { columnCount = 0; if (calcAverage) { averageColumn = columnCount; columnCount++; } else { averageColumn = -1; } if (calcMin) { minColumn = columnCount; columnCount++; } else { minColumn = -1; } if (calcMax) { maxColumn = columnCount; columnCount++; } else { maxColumn = -1; } if (calcVariance) { varianceColumn = columnCount; columnCount++; } else { varianceColumn = -1; } if (calcStandardDeviation) { standardDeviationColumn = columnCount; columnCount++; } else { standardDeviationColumn = -1; } if (calcMedian) { medianColumn = columnCount; columnCount++; } else { medianColumn = -1; } this.detailTableHeaderStrings = new String[columnCount + 1]; this.detailTableHeaderStrings[0] = Settings.getInstance().getResourceBundle().getString("detailLabelFeature"); if (calcAverage) { this.detailTableHeaderStrings[averageColumn + 1] = Settings.getInstance().getResourceBundle().getString( "detailLabelAverage"); } if (calcMin) { this.detailTableHeaderStrings[minColumn + 1] = Settings.getInstance().getResourceBundle().getString( "detailLabelMin"); } if (calcMax) { this.detailTableHeaderStrings[maxColumn + 1] = Settings.getInstance().getResourceBundle().getString( "detailLabelMax"); } if (calcVariance) { this.detailTableHeaderStrings[varianceColumn + 1] = Settings.getInstance().getResourceBundle().getString( "detailLabelVariance"); } if (calcStandardDeviation) { this.detailTableHeaderStrings[standardDeviationColumn + 1] = Settings.getInstance().getResourceBundle() .getString("detailLabelStandardDeviation"); } if (calcMedian) { this.detailTableHeaderStrings[medianColumn + 1] = Settings.getInstance().getResourceBundle().getString( "detailLabelMedian"); } } /** * Set if the minimum will be calculated. * * @param calcMin * new calcMin value. */ public void setCalcMin(boolean calcMin) { this.calcMin = calcMin; this.update(null, null); } /** * Returns true if minimum is calculated. * * @return true if minimum is calculated. */ public boolean isCalcMin() { return this.calcMin; } /** * Set if the maximum will be calculated. * * @param calcMax * new calcMax value. */ public void setCalcMax(boolean calcMax) { this.calcMax = calcMax; this.update(null, null); } /** * Returns true if the maximum is calculated. * * @return true if the maximum is calculated. */ public boolean isCalcMax() { return this.calcMax; } /** * Set if the average is calculated. * * @param calcAverage * new calcAverage value. */ public void setCalcAverage(boolean calcAverage) { this.calcAverage = calcAverage; this.update(null, null); } /** * Returns true if average is calculated. * * @return true if average is calculated. */ public boolean isCalcAverage() { return this.calcAverage; } /** * Set if the variance is calculated. * * @param calcVariance * new calcVariance value. */ public void setCalcVariance(boolean calcVariance) { this.calcVariance = calcVariance; this.update(null, null); } /** * Returns true if variance is calculated. * * @return true if variance is calculated. */ public boolean isCalcVariance() { return this.calcVariance; } /** * Set if the standard deviation is calculated. * * @param calcStandardDeviation * new calcStandardDeviation value. */ public void setCalcStandardDeviation(boolean calcStandardDeviation) { this.calcStandardDeviation = calcStandardDeviation; this.update(null, null); } /** * Returns true if standard deviation is calculated. * * @return true if standard deviation is calculated. */ public boolean isCalcStandardDeviation() { return this.calcStandardDeviation; } /** * Set if median is calculated. * * @param calcMedian * new calcMedian value. */ public void setCalcMedian(boolean calcMedian) { this.calcMedian = calcMedian; this.update(null, null); } /** * Returns true if median is calculated. * * @return true if median is calculated. */ public boolean isCalcMedian() { return this.calcMedian; } /** * Shows error message if an exception occurred. */ private void showErrorMessage() { JOptionPane.showMessageDialog(this, Settings.getInstance().getResourceBundle().getString("detailError")); this.detailPanel = new DetailPanel(this, null, null, 0, null); } }