/* * Copyright 1999-2005 The Apache Software Foundation. * * Licensed 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.lf5.viewer; import org.apache.log4j.lf5.LogRecord; import org.apache.log4j.lf5.LogRecordFilter; import org.apache.log4j.lf5.PassingLogRecordFilter; import javax.swing.table.AbstractTableModel; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; /** * A TableModel for LogRecords which includes filtering support. * * @author Richard Wan * @author Brent Sprecher */ // Contributed by ThoughtWorks Inc. public class FilteredLogTableModel extends AbstractTableModel { //-------------------------------------------------------------------------- // Constants: //-------------------------------------------------------------------------- //-------------------------------------------------------------------------- // Protected Variables: //-------------------------------------------------------------------------- protected LogRecordFilter _filter = new PassingLogRecordFilter(); protected List _allRecords = new ArrayList(); protected List _filteredRecords; protected int _maxNumberOfLogRecords = 5000; protected String[] _colNames = {"Date", "Thread", "Message #", "Level", "NDC", "Category", "Message", "Location", "Thrown"}; //-------------------------------------------------------------------------- // Private Variables: //-------------------------------------------------------------------------- //-------------------------------------------------------------------------- // Constructors: //-------------------------------------------------------------------------- public FilteredLogTableModel() { super(); } //-------------------------------------------------------------------------- // Public Methods: //-------------------------------------------------------------------------- public void setLogRecordFilter(LogRecordFilter filter) { _filter = filter; } public LogRecordFilter getLogRecordFilter() { return _filter; } public String getColumnName(int i) { return _colNames[i]; } public int getColumnCount() { return _colNames.length; } public int getRowCount() { return getFilteredRecords().size(); } public int getTotalRowCount() { return _allRecords.size(); } public Object getValueAt(int row, int col) { LogRecord record = getFilteredRecord(row); return getColumn(col, record); } public void setMaxNumberOfLogRecords(int maxNumRecords) { if (maxNumRecords > 0) { _maxNumberOfLogRecords = maxNumRecords; } } public synchronized boolean addLogRecord(LogRecord record) { _allRecords.add(record); if (_filter.passes(record) == false) { return false; } getFilteredRecords().add(record); fireTableRowsInserted(getRowCount(), getRowCount()); trimRecords(); return true; } /** * Forces the LogTableModel to requery its filters to determine * which records to display. */ public synchronized void refresh() { _filteredRecords = createFilteredRecordsList(); fireTableDataChanged(); } public synchronized void fastRefresh() { _filteredRecords.remove(0); fireTableRowsDeleted(0, 0); } /** * Clears all records from the LogTableModel */ public synchronized void clear() { _allRecords.clear(); _filteredRecords.clear(); fireTableDataChanged(); } //-------------------------------------------------------------------------- // Protected Methods: //-------------------------------------------------------------------------- protected List getFilteredRecords() { if (_filteredRecords == null) { refresh(); } return _filteredRecords; } protected List createFilteredRecordsList() { List result = new ArrayList(); Iterator records = _allRecords.iterator(); LogRecord current; while (records.hasNext()) { current = (LogRecord) records.next(); if (_filter.passes(current)) { result.add(current); } } return result; } protected LogRecord getFilteredRecord(int row) { List records = getFilteredRecords(); int size = records.size(); if (row < size) { return (LogRecord) records.get(row); } // a minor problem has happened. JTable has asked for // a row outside the bounds, because the size of // _filteredRecords has changed while it was looping. // return the last row. return (LogRecord) records.get(size - 1); } protected Object getColumn(int col, LogRecord lr) { if (lr == null) { return "NULL Column"; } String date = new Date(lr.getMillis()).toString(); switch (col) { case 0: return date + " (" + lr.getMillis() + ")"; case 1: return lr.getThreadDescription(); case 2: return new Long(lr.getSequenceNumber()); case 3: return lr.getLevel(); case 4: return lr.getNDC(); case 5: return lr.getCategory(); case 6: return lr.getMessage(); case 7: return lr.getLocation(); case 8: return lr.getThrownStackTrace(); default: String message = "The column number " + col + "must be between 0 and 8"; throw new IllegalArgumentException(message); } } // We don't want the amount of rows to grow without bound, // leading to a out-of-memory-exception. Especially not good // in a production environment :) // This method & clearLogRecords() are synchronized so we don't // delete rows that don't exist. protected void trimRecords() { if (needsTrimming()) { trimOldestRecords(); } } protected boolean needsTrimming() { return (_allRecords.size() > _maxNumberOfLogRecords); } protected void trimOldestRecords() { synchronized (_allRecords) { int trim = numberOfRecordsToTrim(); if (trim > 1) { List oldRecords = _allRecords.subList(0, trim); oldRecords.clear(); refresh(); } else { _allRecords.remove(0); fastRefresh(); } } } //-------------------------------------------------------------------------- // Private Methods: //-------------------------------------------------------------------------- private int numberOfRecordsToTrim() { return _allRecords.size() - _maxNumberOfLogRecords; } //-------------------------------------------------------------------------- // Nested Top-Level Classes or Interfaces //-------------------------------------------------------------------------- }