package com.onaro.sanscreen.client.view.tabular; import java.awt.Color; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.MissingResourceException; import java.util.ResourceBundle; import javax.swing.JComponent; import javax.swing.JTable; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import com.onaro.commons.swing.table.OnaroTableModel; import com.onaro.util.jfc.grouping.GroupingTable; public abstract class OnaroHighlight implements HighlightInterface { static final Logger logger = LogManager.getLogger(OnaroHighlight.class); /** * Caches color by style. Initialized gradually as the styles are being used. */ private static final Map colorByStyleName = new HashMap(); private static final ResourceBundle resources = ResourceBundle.getBundle("com.onaro.sanscreen.client.Highlight"); //$NON-NLS-1$ private Color foreground; private Color background; private Color selectedForeground; private Color selectedBackground; /** * If true the highlight is always on regardless of the row being evaluated */ private boolean alwaysOn = false; private boolean shouldReset = false; /** * Flag to indicate whether the highlight should be propogated up from rows * to the group summary row. */ private boolean groupHighlight = true; private boolean cached = true; private OnaroTableModel tableModel; /** * Cache for highlight evaluation. The use of the cache can be enabled/disabled through * setCached() */ private Map evaluationCache = new HashMap(); private MyTableModelListener cacheListener = null; public OnaroHighlight(OnaroTableModel tableModel) { this(tableModel, true); } public OnaroHighlight(OnaroTableModel tableModel, boolean cached) { if (tableModel == null) throw new IllegalArgumentException("tableModel"); //$NON-NLS-1$ this.tableModel = tableModel; this.cached = cached; } protected OnaroTableModel getTableModel() { return tableModel; } public abstract boolean shouldApply(R row); private boolean shouldApply(JTable table, int row) { Integer rowInteger = Integer.valueOf(row); if (isCached()) { Boolean cachedValue = evaluationCache.get(rowInteger); if (cachedValue != null) { return cachedValue.booleanValue(); } } boolean shouldApply = shouldApplyImpl(table, row); if (isCached()) { registerCacheListener(table); evaluationCache.put(rowInteger, Boolean.valueOf(shouldApply)); } return shouldApply; } private boolean shouldApplyImpl(JTable table, int row) { List actualRows; if (table instanceof GroupingTable) { actualRows = ((GroupingTable)table).getChildRowsInEncapsulatedModel(row); } else { actualRows = Collections.singletonList(Integer.valueOf(row)); } // Check to see if this is a grouping row, and don't apply if this // isn't a grouping highlight if (actualRows.size() > 1 && !isGroupHighlight()) { return false; } boolean shouldApply = false; for (Integer actualRow : actualRows) { int actualRowNumber = actualRow.intValue(); List tableRows = getTableModel().getRows(); if (actualRowNumber >= tableRows.size()) { // actualRow no longer exists. logger.warn("Attempt to highlight row that no longer exists: " //$NON-NLS-1$ + actualRowNumber + " > " + tableRows.size()); //$NON-NLS-1$ break; } R rowValue = tableRows.get(actualRowNumber); shouldApply = shouldApply(rowValue); if (shouldApply) { break; } } return shouldApply; } /** * Register the cache listener if necessary * @param table the table to listen to */ private void registerCacheListener(JTable table) { if (cacheListener == null) { cacheListener = new MyTableModelListener(); table.getModel().addTableModelListener(cacheListener); } } private class MyTableModelListener implements TableModelListener { public void tableChanged(TableModelEvent e) { evaluationCache.clear(); } } public JComponent apply(JTable table, JComponent compRenderer, int row) { if (shouldReset) { compRenderer.setForeground(table.getForeground()); } if (alwaysOn || shouldApply(table, row)) { if (table.isRowSelected(row)) { if (selectedForeground != null) compRenderer.setForeground(selectedForeground); if (selectedBackground != null) compRenderer.setBackground(selectedBackground); } else { if (foreground != null) compRenderer.setForeground(foreground); if (background != null) compRenderer.setBackground(background); } } return compRenderer; } public void setStyleName(String styleName) { setForeground(getColor(styleName + ".foreground")); //$NON-NLS-1$ setBackground(getColor(styleName + ".background")); //$NON-NLS-1$ setSelectedForeground(getColor(styleName + ".selected.foreground")); //$NON-NLS-1$ setSelectedBackground(getColor(styleName + ".selected.background")); //$NON-NLS-1$ } private Color getColor(String colorKey) { if (!colorByStyleName.containsKey(colorKey)) { try { Color color = null; String colorCode = resources.getString(colorKey); if (colorCode != null && colorCode.length() > 0) { color = Color.decode(colorCode); } colorByStyleName.put(colorKey, color); } catch (MissingResourceException e) { logger.error("Failed to get color named '" + colorKey + '\'' + " - " + e.getMessage(), e); //$NON-NLS-1$ //$NON-NLS-2$ } } return colorByStyleName.get(colorKey); } public boolean isShouldReset() { return shouldReset; } public void setShouldReset(boolean shouldReset) { this.shouldReset = shouldReset; } public boolean isAlwaysOn() { return alwaysOn; } public void setAlwaysOn(boolean alwaysOn) { this.alwaysOn = alwaysOn; } public Color getBackground() { return background; } public void setBackground(Color background) { this.background = background; } public Color getForeground() { return foreground; } public void setForeground(Color foreground) { this.foreground = foreground; } public Color getSelectedBackground() { return selectedBackground; } public void setSelectedBackground(Color selectedBackground) { this.selectedBackground = selectedBackground; } public Color getSelectedForeground() { return selectedForeground; } public void setSelectedForeground(Color selectedForeground) { this.selectedForeground = selectedForeground; } public void setCached(boolean cached) { this.cached = cached; } public boolean isCached() { return cached; } /** * Flag to indicate whether the highlight should be propogated up from rows * to the group summary row. * * @return true if the highlight is applied to grouping rows, false otherwise. */ public boolean isGroupHighlight() { return groupHighlight; } /** * Flag to indicate whether the highlight should be propogated up from rows * to the group summary row. * * @param groupHighlight true if the highlight is applied to grouping rows, false otherwise. */ public void setGroupHighlight(boolean groupHighlight) { this.groupHighlight = groupHighlight; } }