blob: a33b3e33766ff61ef48b6c4da666e9fda8a6144f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.log4j.chainsaw;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.swing.ProgressMonitor;
import javax.swing.event.EventListenerList;
import javax.swing.table.AbstractTableModel;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.log4j.chainsaw.color.RuleColorizer;
import org.apache.log4j.chainsaw.helper.SwingHelper;
import org.apache.log4j.helpers.Constants;
import org.apache.log4j.rule.Rule;
import org.apache.log4j.spi.LocationInfo;
import org.apache.log4j.spi.LoggingEvent;
/**
* A CyclicBuffer implementation of the EventContainer.
*
* NOTE: This implementation prevents duplicate rows from being added to the model.
*
* Ignoring duplicates was added to support receivers which may attempt to deliver the same
* event more than once but can be safely ignored (for example, the database receiver
* when set to retrieve in a loop).
*
* @author Paul Smith <psmith@apache.org>
* @author Scott Deboy <sdeboy@apache.org>
* @author Stephen Pain
*
*/
class ChainsawCyclicBufferTableModel extends AbstractTableModel
implements EventContainer, PropertyChangeListener {
private static final int DEFAULT_CAPACITY = 5000;
//cyclic field used internally in this class, but not exposed via the eventcontainer
private boolean cyclic = true;
private int cyclicBufferSize;
//original list of LoggingEventWrapper instances
List unfilteredList;
//filtered list of LoggingEventWrapper instances
List filteredList;
private boolean currentSortAscending;
private int currentSortColumn;
private final EventListenerList eventListenerList = new EventListenerList();
private final List<String> columnNames = new ArrayList<>(ChainsawColumns.getColumnsNames());
private boolean sortEnabled = false;
private boolean reachedCapacity = false;
private final Logger logger = LogManager.getLogger(ChainsawCyclicBufferTableModel.class);
// protected final Object syncLock = new Object();
private final LoggerNameModel loggerNameModelDelegate = new LoggerNameModelSupport();
private final Object mutex = new Object();
//because we may be using a cyclic buffer, if an ID is not provided in the property,
//use and increment this row counter as the ID for each received row
int uniqueRow;
private final Set uniquePropertyKeys = new HashSet();
private Rule ruleMediator;
private final PropertyChangeSupport propertySupport = new PropertyChangeSupport(this);
private RuleColorizer colorizer;
private final String tableModelName;
public ChainsawCyclicBufferTableModel(int cyclicBufferSize, RuleColorizer colorizer, String tableModelName) {
propertySupport.addPropertyChangeListener("cyclic", new ModelChanger());
this.cyclicBufferSize = cyclicBufferSize;
this.colorizer = colorizer;
this.tableModelName = tableModelName;
unfilteredList = new CyclicBufferList(cyclicBufferSize);
filteredList = new CyclicBufferList(cyclicBufferSize);
}
/* (non-Javadoc)
* @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
*/
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getSource() instanceof Rule) {
if (evt.getSource() == ruleMediator && evt.getPropertyName().equals("findRule")) {
if (((RuleMediator) evt.getSource()).isFindRuleRequired()) {
//only refilter if find rule is required
reFilter();
}
} else {
reFilter();
}
}
}
public List<LoggingEventWrapper> getMatchingEvents(Rule rule) {
List<LoggingEventWrapper> list = new ArrayList<>();
List unfilteredCopy;
synchronized (mutex) {
unfilteredCopy = new ArrayList(unfilteredList);
}
for (Object anUnfilteredCopy : unfilteredCopy) {
LoggingEventWrapper loggingEventWrapper = (LoggingEventWrapper) anUnfilteredCopy;
if (rule.evaluate(loggingEventWrapper.getLoggingEvent(), null)) {
list.add(loggingEventWrapper);
}
}
return list;
}
public void reFilter() {
final int previousSize;
final int newSize;
synchronized (mutex) {
//post refilter with newValue of TRUE (filtering is about to begin)
propertySupport.firePropertyChange("refilter", Boolean.FALSE, Boolean.TRUE);
previousSize = filteredList.size();
filteredList.clear();
if (ruleMediator == null) {
LoggingEventWrapper lastEvent = null;
for (Object anUnfilteredList : unfilteredList) {
LoggingEventWrapper loggingEventWrapper = (LoggingEventWrapper) anUnfilteredList;
loggingEventWrapper.setDisplayed(true);
updateEventMillisDelta(loggingEventWrapper, lastEvent);
filteredList.add(loggingEventWrapper);
lastEvent = loggingEventWrapper;
}
} else {
Iterator iter = unfilteredList.iterator();
LoggingEventWrapper lastEvent = null;
while (iter.hasNext()) {
LoggingEventWrapper loggingEventWrapper = (LoggingEventWrapper) iter.next();
if (ruleMediator.evaluate(loggingEventWrapper.getLoggingEvent(), null)) {
loggingEventWrapper.setDisplayed(true);
filteredList.add(loggingEventWrapper);
updateEventMillisDelta(loggingEventWrapper, lastEvent);
lastEvent = loggingEventWrapper;
} else {
loggingEventWrapper.setDisplayed(false);
}
}
}
newSize = filteredList.size();
}
SwingHelper.invokeOnEDT(() -> {
if (newSize > 0) {
if (previousSize == newSize) {
//same - update all
fireTableRowsUpdated(0, newSize - 1);
} else if (previousSize > newSize) {
//less now..update and delete difference
fireTableRowsUpdated(0, newSize - 1);
//swing bug exposed by variable height rows when calling fireTableRowsDeleted..use tabledatacchanged
fireTableDataChanged();
} else if (previousSize < newSize) {
//more now..update and insert difference
if (previousSize > 0) {
fireTableRowsUpdated(0, previousSize - 1);
}
fireTableRowsInserted(Math.max(0, previousSize), newSize - 1);
}
} else {
//no rows to show
fireTableDataChanged();
}
notifyCountListeners();
//post refilter with newValue of FALSE (filtering is complete)
SwingHelper.invokeOnEDT(() -> propertySupport.firePropertyChange("refilter", Boolean.TRUE, Boolean.FALSE));
});
}
public int locate(Rule rule, int startLocation, boolean searchForward) {
List filteredListCopy;
synchronized (mutex) {
filteredListCopy = new ArrayList(filteredList);
}
if (searchForward) {
for (int i = startLocation; i < filteredListCopy.size(); i++) {
if (rule.evaluate(((LoggingEventWrapper) filteredListCopy.get(i)).getLoggingEvent(), null)) {
return i;
}
}
//if there was no match, start at row zero and go to startLocation
for (int i = 0; i < startLocation; i++) {
if (rule.evaluate(((LoggingEventWrapper) filteredListCopy.get(i)).getLoggingEvent(), null)) {
return i;
}
}
} else {
for (int i = startLocation; i > -1; i--) {
if (rule.evaluate(((LoggingEventWrapper) filteredListCopy.get(i)).getLoggingEvent(), null)) {
return i;
}
}
//if there was no match, start at row list.size() - 1 and go to startLocation
for (int i = filteredListCopy.size() - 1; i > startLocation; i--) {
if (rule.evaluate(((LoggingEventWrapper) filteredListCopy.get(i)).getLoggingEvent(), null)) {
return i;
}
}
}
return -1;
}
/**
* @param l
*/
public void removeLoggerNameListener(LoggerNameListener l) {
loggerNameModelDelegate.removeLoggerNameListener(l);
}
/**
* @param loggerName
* @return
*/
public boolean addLoggerName(String loggerName) {
return loggerNameModelDelegate.addLoggerName(loggerName);
}
public String toString() {
return "ChainsawCyclicBufferTableModel{" +
"name='" + tableModelName + '\'' +
'}';
}
public void reset() {
loggerNameModelDelegate.reset();
}
/**
* @param l
*/
public void addLoggerNameListener(LoggerNameListener l) {
loggerNameModelDelegate.addLoggerNameListener(l);
}
/**
* @return
*/
public Collection getLoggerNames() {
return loggerNameModelDelegate.getLoggerNames();
}
public void addEventCountListener(EventCountListener listener) {
eventListenerList.add(EventCountListener.class, listener);
}
public boolean isSortable(int col) {
return true;
}
public void notifyCountListeners() {
EventCountListener[] listeners = eventListenerList.getListeners(EventCountListener.class);
int filteredListSize;
int unfilteredListSize;
synchronized (mutex) {
filteredListSize = filteredList.size();
unfilteredListSize = unfilteredList.size();
}
for (EventCountListener listener : listeners) {
listener.eventCountChanged(
filteredListSize, unfilteredListSize);
}
}
/**
* Changes the underlying display rule in use. If there was
* a previous Rule defined, this Model removes itself as a listener
* from the old rule, and adds itself to the new rule (if the new Rule is not Null).
*
* In any case, the model ensures the Filtered list is made up to date in a separate thread.
*/
public void setRuleMediator(RuleMediator ruleMediator) {
if (this.ruleMediator != null) {
this.ruleMediator.removePropertyChangeListener(this);
}
this.ruleMediator = ruleMediator;
if (this.ruleMediator != null) {
this.ruleMediator.addPropertyChangeListener(this);
}
reFilter();
}
/* (non-Javadoc)
* @see org.apache.log4j.chainsaw.EventContainer#sort()
*/
public void sort() {
boolean sort;
final int filteredListSize;
synchronized (mutex) {
filteredListSize = filteredList.size();
sort = (sortEnabled && filteredListSize > 0);
if (sort) {
//reset display (used to ensure row height is updated)
LoggingEventWrapper lastEvent = null;
for (Object aFilteredList : filteredList) {
LoggingEventWrapper e = (LoggingEventWrapper) aFilteredList;
e.setDisplayed(true);
updateEventMillisDelta(e, lastEvent);
lastEvent = e;
}
filteredList.sort(new ColumnComparator(
getColumnName(currentSortColumn), currentSortColumn,
currentSortAscending));
}
}
if (sort) {
SwingHelper.invokeOnEDT(() -> fireTableRowsUpdated(0, Math.max(filteredListSize - 1, 0)));
}
}
public boolean isSortEnabled() {
return sortEnabled;
}
public void sortColumn(int col, boolean ascending) {
logger.debug("request to sort col=" + col);
currentSortAscending = ascending;
currentSortColumn = col;
sortEnabled = true;
sort();
}
/* (non-Javadoc)
* @see org.apache.log4j.chainsaw.EventContainer#clear()
*/
public void clearModel() {
reachedCapacity = false;
synchronized (mutex) {
unfilteredList.clear();
filteredList.clear();
uniqueRow = 0;
}
SwingHelper.invokeOnEDT(this::fireTableDataChanged);
notifyCountListeners();
loggerNameModelDelegate.reset();
}
public List getAllEvents() {
synchronized (mutex) {
return new ArrayList(unfilteredList);
}
}
public List getFilteredEvents() {
synchronized (mutex) {
return new ArrayList(filteredList);
}
}
public int getRowIndex(LoggingEventWrapper loggingEventWrapper) {
synchronized (mutex) {
return filteredList.indexOf(loggingEventWrapper);
}
}
public void removePropertyFromEvents(String propName) {
//first remove the event from any displayed events, so we can fire row updated event
List filteredListCopy;
List unfilteredListCopy;
synchronized(mutex) {
filteredListCopy = new ArrayList(filteredList);
unfilteredListCopy = new ArrayList(unfilteredList);
}
for (int i=0;i<filteredListCopy.size();i++) {
LoggingEventWrapper loggingEventWrapper = (LoggingEventWrapper)filteredListCopy.get(i);
Object result = loggingEventWrapper.removeProperty(propName);
if (result != null) {
fireRowUpdated(i, false);
}
}
//now remove the event from all events
for (Object anUnfilteredListCopy : unfilteredListCopy) {
LoggingEventWrapper loggingEventWrapper = (LoggingEventWrapper) anUnfilteredListCopy;
loggingEventWrapper.removeProperty(propName);
}
}
public int updateEventsWithFindRule(Rule findRule) {
int count = 0;
List unfilteredListCopy;
synchronized(mutex) {
unfilteredListCopy = new ArrayList(unfilteredList);
}
for (Object anUnfilteredListCopy : unfilteredListCopy) {
LoggingEventWrapper loggingEventWrapper = (LoggingEventWrapper) anUnfilteredListCopy;
loggingEventWrapper.evaluateSearchRule(findRule);
//return the count of visible search matches
if (loggingEventWrapper.isSearchMatch() && loggingEventWrapper.isDisplayed()) {
count++;
}
}
return count;
}
public int findColoredRow(int startLocation, boolean searchForward) {
List filteredListCopy;
synchronized (mutex) {
filteredListCopy = new ArrayList(filteredList);
}
if (searchForward) {
for (int i = startLocation; i < filteredListCopy.size(); i++) {
LoggingEventWrapper event = (LoggingEventWrapper)filteredListCopy.get(i);
if (!event.getColorRuleBackground().equals(ChainsawConstants.COLOR_DEFAULT_BACKGROUND) ||
!event.getColorRuleForeground().equals(ChainsawConstants.COLOR_DEFAULT_FOREGROUND)) {
return i;
}
}
//searching forward, no colorized event was found - now start at row zero and go to startLocation
for (int i = 0; i < startLocation; i++) {
LoggingEventWrapper event = (LoggingEventWrapper)filteredListCopy.get(i);
if (!event.getColorRuleBackground().equals(ChainsawConstants.COLOR_DEFAULT_BACKGROUND) ||
!event.getColorRuleForeground().equals(ChainsawConstants.COLOR_DEFAULT_FOREGROUND)) {
return i;
}
}
} else {
for (int i = startLocation; i > -1; i--) {
LoggingEventWrapper event = (LoggingEventWrapper)filteredListCopy.get(i);
if (!event.getColorRuleBackground().equals(ChainsawConstants.COLOR_DEFAULT_BACKGROUND) ||
!event.getColorRuleForeground().equals(ChainsawConstants.COLOR_DEFAULT_FOREGROUND)) {
return i;
}
}
//searching backward, no colorized event was found - now start at list.size() - 1 and go to startLocation
for (int i = filteredListCopy.size() - 1; i > startLocation; i--) {
LoggingEventWrapper event = (LoggingEventWrapper)filteredListCopy.get(i);
if (!event.getColorRuleBackground().equals(ChainsawConstants.COLOR_DEFAULT_BACKGROUND) ||
!event.getColorRuleForeground().equals(ChainsawConstants.COLOR_DEFAULT_FOREGROUND)) {
return i;
}
}
}
return -1;
}
public int getSearchMatchCount() {
int searchMatchCount = 0;
synchronized(mutex) {
for (Object aFilteredList : filteredList) {
LoggingEventWrapper wrapper = (LoggingEventWrapper) aFilteredList;
if (wrapper.isSearchMatch() && wrapper.isDisplayed()) {
searchMatchCount++;
}
}
}
return searchMatchCount;
}
public int getColumnCount() {
return columnNames.size();
}
public String getColumnName(int column) {
return columnNames.get(column);
}
public LoggingEventWrapper getRow(int row) {
synchronized (mutex) {
if (row < filteredList.size() && row > -1) {
return (LoggingEventWrapper) filteredList.get(row);
}
}
return null;
}
public int getRowCount() {
synchronized (mutex) {
return filteredList.size();
}
}
public Object getValueAt(int rowIndex, int columnIndex) {
LoggingEvent event = null;
synchronized (mutex) {
if (rowIndex < filteredList.size() && rowIndex > -1) {
event = ((LoggingEventWrapper) filteredList.get(rowIndex)).getLoggingEvent();
}
}
if (event == null) {
return null;
}
LocationInfo info = null;
if (event.locationInformationExists()) {
info = event.getLocationInformation();
}
switch (columnIndex + 1) {
case ChainsawColumns.INDEX_ID_COL_NAME:
Object id = event.getProperty(Constants.LOG4J_ID_KEY);
if (id != null) {
return id;
}
return rowIndex;
case ChainsawColumns.INDEX_LEVEL_COL_NAME:
return event.getLevel();
case ChainsawColumns.INDEX_LOG4J_MARKER_COL_NAME:
return event.getProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE);
case ChainsawColumns.INDEX_MILLIS_DELTA_COL_NAME:
return event.getProperty(ChainsawConstants.MILLIS_DELTA_COL_NAME_LOWERCASE);
case ChainsawColumns.INDEX_LOGGER_COL_NAME:
return event.getLoggerName();
case ChainsawColumns.INDEX_TIMESTAMP_COL_NAME:
return new Date(event.getTimeStamp());
case ChainsawColumns.INDEX_MESSAGE_COL_NAME:
return event.getRenderedMessage();
case ChainsawColumns.INDEX_NDC_COL_NAME:
return event.getNDC();
case ChainsawColumns.INDEX_THREAD_COL_NAME:
return event.getThreadName();
case ChainsawColumns.INDEX_THROWABLE_COL_NAME:
return event.getThrowableStrRep();
case ChainsawColumns.INDEX_CLASS_COL_NAME:
return ((info == null) || ("?".equals(info.getClassName()))) ? "" : info.getClassName();
case ChainsawColumns.INDEX_FILE_COL_NAME:
return ((info == null) || ("?".equals(info.getFileName()))) ? "" : info.getFileName();
case ChainsawColumns.INDEX_LINE_COL_NAME:
return ((info == null) || ("?".equals(info.getLineNumber()))) ? "" : info.getLineNumber();
case ChainsawColumns.INDEX_METHOD_COL_NAME:
return ((info == null) || ("?".equals(info.getMethodName()))) ? "" : info.getMethodName();
default:
if (columnIndex < columnNames.size()) {
//case may not match..try case sensitive and fall back to case-insensitive
String result = event.getProperty(columnNames.get(columnIndex).toString());
if (result == null) {
String lowerColName = columnNames.get(columnIndex).toString().toLowerCase(Locale.ENGLISH);
Set<Map.Entry> entrySet = event.getProperties().entrySet();
for (Object anEntrySet : entrySet) {
Map.Entry thisEntry = (Map.Entry) anEntrySet;
if (thisEntry.getKey().toString().equalsIgnoreCase(lowerColName)) {
result = thisEntry.getValue().toString();
}
}
}
if (result != null) {
return result;
}
}
}
return "";
}
public boolean isAddRow(LoggingEventWrapper loggingEventWrapper) {
Object id = loggingEventWrapper.getLoggingEvent().getProperty(Constants.LOG4J_ID_KEY);
//only set the property if it doesn't already exist
if (id == null) {
id = ++uniqueRow;
loggingEventWrapper.setProperty(Constants.LOG4J_ID_KEY, id.toString());
}
loggingEventWrapper.updateColorRuleColors(colorizer.getBackgroundColor(loggingEventWrapper.getLoggingEvent()), colorizer.getForegroundColor(loggingEventWrapper.getLoggingEvent()));
Rule findRule = colorizer.getFindRule();
if (findRule != null) {
loggingEventWrapper.evaluateSearchRule(colorizer.getFindRule());
}
boolean rowAdded = false;
/**
* If we're in cyclic mode and over budget on the size, the addition of a new event will
* cause the oldest event to fall off the cliff. We need to remove that events ID from the
* Set so we are not keeping track of IDs for all events ever received (we'd run out of
* memory...)
*/
synchronized(mutex) {
if (cyclic) {
CyclicBufferList bufferList = (CyclicBufferList) unfilteredList;
if (bufferList.size() == bufferList.getMaxSize()) {
reachedCapacity = true;
}
}
int unfilteredSize = unfilteredList.size();
LoggingEventWrapper lastLoggingEventWrapper = null;
if (unfilteredSize > 0) {
lastLoggingEventWrapper = (LoggingEventWrapper) unfilteredList.get(unfilteredSize - 1);
}
unfilteredList.add(loggingEventWrapper);
if ((ruleMediator == null) || (ruleMediator.evaluate(loggingEventWrapper.getLoggingEvent(), null))) {
loggingEventWrapper.setDisplayed(true);
updateEventMillisDelta(loggingEventWrapper, lastLoggingEventWrapper);
filteredList.add(loggingEventWrapper);
rowAdded = true;
} else {
loggingEventWrapper.setDisplayed(false);
}
}
checkForNewColumn(loggingEventWrapper);
return rowAdded;
}
private void updateEventMillisDelta(LoggingEventWrapper loggingEventWrapper, LoggingEventWrapper lastLoggingEventWrapper) {
if (lastLoggingEventWrapper != null) {
loggingEventWrapper.setPreviousDisplayedEventTimestamp(lastLoggingEventWrapper.getLoggingEvent().getTimeStamp());
} else {
//delta to same event = 0
loggingEventWrapper.setPreviousDisplayedEventTimestamp(loggingEventWrapper.getLoggingEvent().getTimeStamp());
}
}
private void checkForNewColumn(LoggingEventWrapper loggingEventWrapper)
{
/**
* Is this a new Property key we haven't seen before? Remember that now MDC has been merged
* into the Properties collection
*/
boolean newColumn = uniquePropertyKeys.addAll(loggingEventWrapper.getPropertyKeySet());
if (newColumn) {
/**
* If so, we should add them as columns and notify listeners.
*/
for (Object o : loggingEventWrapper.getPropertyKeySet()) {
String key = o.toString().toUpperCase();
//add all keys except the 'log4jid' key (columnNames is all-caps)
if (!columnNames.contains(key) && !(Constants.LOG4J_ID_KEY.equalsIgnoreCase(key))) {
columnNames.add(key);
logger.debug("Adding col '" + key + "', columnNames=" + columnNames);
fireNewKeyColumnAdded(
new NewKeyEvent(
this, columnNames.indexOf(key), key, loggingEventWrapper.getLoggingEvent().getProperty(key)));
}
}
}
}
public void fireTableEvent(final int begin, final int end, final int count) {
SwingHelper.invokeOnEDT(() -> {
if (cyclic) {
if (!reachedCapacity) {
//if we didn't loop and it's the 1st time, insert
if ((begin + count) < cyclicBufferSize) {
fireTableRowsInserted(begin, end);
} else {
//we did loop - insert and then update rows
//rows are zero-indexed, subtract 1 from cyclicbuffersize for the event notification
fireTableRowsInserted(begin, cyclicBufferSize - 1);
fireTableRowsUpdated(0, cyclicBufferSize - 1);
reachedCapacity = true;
}
} else {
fireTableRowsUpdated(0, cyclicBufferSize - 1);
}
} else {
fireTableRowsInserted(begin, end);
}
});
}
public void fireRowUpdated(int row, boolean checkForNewColumns) {
LoggingEventWrapper loggingEventWrapper = getRow(row);
if (loggingEventWrapper != null)
{
loggingEventWrapper.updateColorRuleColors(colorizer.getBackgroundColor(loggingEventWrapper.getLoggingEvent()), colorizer.getForegroundColor(loggingEventWrapper.getLoggingEvent()));
Rule findRule = colorizer.getFindRule();
if (findRule != null) {
loggingEventWrapper.evaluateSearchRule(colorizer.getFindRule());
}
fireTableRowsUpdated(row, row);
if (checkForNewColumns) {
//row may have had a column added..if so, make sure a column is added
checkForNewColumn(loggingEventWrapper);
}
}
}
/**
* @param e
*/
private void fireNewKeyColumnAdded(NewKeyEvent e) {
NewKeyListener[] listeners =
eventListenerList.getListeners(NewKeyListener.class);
for (NewKeyListener listener : listeners) {
listener.newKeyAdded(e);
}
}
/**
* @return
*/
public int getMaxSize() {
return cyclicBufferSize;
}
/* (non-Javadoc)
* @see org.apache.log4j.chainsaw.EventContainer#addNewKeyListener(org.apache.log4j.chainsaw.NewKeyListener)
*/
public void addNewKeyListener(NewKeyListener l) {
eventListenerList.add(NewKeyListener.class, l);
}
/* (non-Javadoc)
* @see org.apache.log4j.chainsaw.EventContainer#removeNewKeyListener(org.apache.log4j.chainsaw.NewKeyListener)
*/
public void removeNewKeyListener(NewKeyListener l) {
eventListenerList.remove(NewKeyListener.class, l);
}
/* (non-Javadoc)
* @see javax.swing.table.TableModel#isCellEditable(int, int)
*/
public boolean isCellEditable(int rowIndex, int columnIndex) {
if (getColumnName(columnIndex).equalsIgnoreCase(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE)) {
return true;
}
return columnIndex < columnNames.size() && super.isCellEditable(rowIndex, columnIndex);
}
/* (non-Javadoc)
* @see org.apache.log4j.chainsaw.EventContainer#setCyclic(boolean)
*/
public void setCyclic(final boolean cyclic) {
if (this.cyclic == cyclic) {
return;
}
final boolean old = this.cyclic;
this.cyclic = cyclic;
propertySupport.firePropertyChange("cyclic", old, cyclic);
}
/* (non-Javadoc)
* @see org.apache.log4j.chainsaw.EventContainer#addPropertyChangeListener(java.beans.PropertyChangeListener)
*/
public void addPropertyChangeListener(PropertyChangeListener l) {
propertySupport.addPropertyChangeListener(l);
}
/* (non-Javadoc)
* @see org.apache.log4j.chainsaw.EventContainer#addPropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)
*/
public void addPropertyChangeListener(
String propertyName, PropertyChangeListener l) {
propertySupport.addPropertyChangeListener(propertyName, l);
}
/* (non-Javadoc)
* @see org.apache.log4j.chainsaw.EventContainer#size()
*/
public int size() {
synchronized(mutex) {
return unfilteredList.size();
}
}
private class ModelChanger implements PropertyChangeListener {
/* (non-Javadoc)
* @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
*/
public void propertyChange(PropertyChangeEvent arg0) {
Thread thread =
new Thread(
() -> {
ProgressMonitor monitor = null;
int index = 0;
try {
synchronized (mutex) {
monitor =
new ProgressMonitor(
null, "Switching models...",
"Transferring between data structures, please wait...", 0,
unfilteredList.size() + 1);
monitor.setMillisToDecideToPopup(250);
monitor.setMillisToPopup(100);
logger.debug(
"Changing Model, isCyclic is now " + cyclic);
List newUnfilteredList;
List newFilteredList;
if (cyclic) {
newUnfilteredList = new CyclicBufferList(cyclicBufferSize);
newFilteredList = new CyclicBufferList(cyclicBufferSize);
} else {
newUnfilteredList = new ArrayList(cyclicBufferSize);
newFilteredList = new ArrayList(cyclicBufferSize);
}
int increment = 0;
for (Object anUnfilteredList : unfilteredList) {
LoggingEventWrapper loggingEventWrapper = (LoggingEventWrapper) anUnfilteredList;
newUnfilteredList.add(loggingEventWrapper);
monitor.setProgress(index++);
}
unfilteredList = newUnfilteredList;
filteredList = newFilteredList;
}
monitor.setNote("Refiltering...");
reFilter();
monitor.setProgress(index++);
} finally {
monitor.close();
}
logger.debug("Model Change completed");
});
thread.setPriority(Thread.MIN_PRIORITY + 1);
thread.start();
}
}
}