001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.log4j.lf5.viewer; 018 019import org.apache.log4j.lf5.LogRecord; 020import org.apache.log4j.lf5.LogRecordFilter; 021import org.apache.log4j.lf5.PassingLogRecordFilter; 022 023import javax.swing.table.AbstractTableModel; 024import java.util.ArrayList; 025import java.util.Date; 026import java.util.Iterator; 027import java.util.List; 028 029 030/** 031 * A TableModel for LogRecords which includes filtering support. 032 * 033 * @author Richard Wan 034 * @author Brent Sprecher 035 */ 036 037// Contributed by ThoughtWorks Inc. 038 039public class FilteredLogTableModel 040 extends AbstractTableModel { 041 //-------------------------------------------------------------------------- 042 // Constants: 043 //-------------------------------------------------------------------------- 044 045 //-------------------------------------------------------------------------- 046 // Protected Variables: 047 //-------------------------------------------------------------------------- 048 049 protected LogRecordFilter _filter = new PassingLogRecordFilter(); 050 protected List _allRecords = new ArrayList(); 051 protected List _filteredRecords; 052 protected int _maxNumberOfLogRecords = 5000; 053 protected String[] _colNames = {"Date", 054 "Thread", 055 "Message #", 056 "Level", 057 "NDC", 058 "Category", 059 "Message", 060 "Location", 061 "Thrown"}; 062 063 //-------------------------------------------------------------------------- 064 // Private Variables: 065 //-------------------------------------------------------------------------- 066 067 //-------------------------------------------------------------------------- 068 // Constructors: 069 //-------------------------------------------------------------------------- 070 071 public FilteredLogTableModel() { 072 super(); 073 } 074 075 //-------------------------------------------------------------------------- 076 // Public Methods: 077 //-------------------------------------------------------------------------- 078 079 public void setLogRecordFilter(LogRecordFilter filter) { 080 _filter = filter; 081 } 082 083 public LogRecordFilter getLogRecordFilter() { 084 return _filter; 085 } 086 087 public String getColumnName(int i) { 088 return _colNames[i]; 089 } 090 091 public int getColumnCount() { 092 return _colNames.length; 093 } 094 095 public int getRowCount() { 096 return getFilteredRecords().size(); 097 } 098 099 public int getTotalRowCount() { 100 return _allRecords.size(); 101 } 102 103 public Object getValueAt(int row, int col) { 104 LogRecord record = getFilteredRecord(row); 105 return getColumn(col, record); 106 } 107 108 public void setMaxNumberOfLogRecords(int maxNumRecords) { 109 if (maxNumRecords > 0) { 110 _maxNumberOfLogRecords = maxNumRecords; 111 } 112 113 } 114 115 public synchronized boolean addLogRecord(LogRecord record) { 116 117 _allRecords.add(record); 118 119 if (_filter.passes(record) == false) { 120 return false; 121 } 122 getFilteredRecords().add(record); 123 fireTableRowsInserted(getRowCount(), getRowCount()); 124 trimRecords(); 125 return true; 126 } 127 128 /** 129 * Forces the LogTableModel to requery its filters to determine 130 * which records to display. 131 */ 132 public synchronized void refresh() { 133 _filteredRecords = createFilteredRecordsList(); 134 fireTableDataChanged(); 135 } 136 137 public synchronized void fastRefresh() { 138 _filteredRecords.remove(0); 139 fireTableRowsDeleted(0, 0); 140 } 141 142 143 /** 144 * Clears all records from the LogTableModel 145 */ 146 public synchronized void clear() { 147 _allRecords.clear(); 148 _filteredRecords.clear(); 149 fireTableDataChanged(); 150 } 151 152 //-------------------------------------------------------------------------- 153 // Protected Methods: 154 //-------------------------------------------------------------------------- 155 156 protected List getFilteredRecords() { 157 if (_filteredRecords == null) { 158 refresh(); 159 } 160 return _filteredRecords; 161 } 162 163 protected List createFilteredRecordsList() { 164 List result = new ArrayList(); 165 Iterator records = _allRecords.iterator(); 166 LogRecord current; 167 while (records.hasNext()) { 168 current = (LogRecord) records.next(); 169 if (_filter.passes(current)) { 170 result.add(current); 171 } 172 } 173 return result; 174 } 175 176 protected LogRecord getFilteredRecord(int row) { 177 List records = getFilteredRecords(); 178 int size = records.size(); 179 if (row < size) { 180 return (LogRecord) records.get(row); 181 } 182 // a minor problem has happened. JTable has asked for 183 // a row outside the bounds, because the size of 184 // _filteredRecords has changed while it was looping. 185 // return the last row. 186 return (LogRecord) records.get(size - 1); 187 188 } 189 190 protected Object getColumn(int col, LogRecord lr) { 191 if (lr == null) { 192 return "NULL Column"; 193 } 194 String date = new Date(lr.getMillis()).toString(); 195 switch (col) { 196 case 0: 197 return date + " (" + lr.getMillis() + ")"; 198 case 1: 199 return lr.getThreadDescription(); 200 case 2: 201 return new Long(lr.getSequenceNumber()); 202 case 3: 203 return lr.getLevel(); 204 case 4: 205 return lr.getNDC(); 206 case 5: 207 return lr.getCategory(); 208 case 6: 209 return lr.getMessage(); 210 case 7: 211 return lr.getLocation(); 212 case 8: 213 return lr.getThrownStackTrace(); 214 default: 215 String message = "The column number " + col + "must be between 0 and 8"; 216 throw new IllegalArgumentException(message); 217 } 218 } 219 220 // We don't want the amount of rows to grow without bound, 221 // leading to a out-of-memory-exception. Especially not good 222 // in a production environment :) 223 224 // This method & clearLogRecords() are synchronized so we don't 225 // delete rows that don't exist. 226 protected void trimRecords() { 227 if (needsTrimming()) { 228 trimOldestRecords(); 229 } 230 } 231 232 protected boolean needsTrimming() { 233 return (_allRecords.size() > _maxNumberOfLogRecords); 234 } 235 236 protected void trimOldestRecords() { 237 synchronized (_allRecords) { 238 int trim = numberOfRecordsToTrim(); 239 if (trim > 1) { 240 List oldRecords = 241 _allRecords.subList(0, trim); 242 oldRecords.clear(); 243 refresh(); 244 } else { 245 _allRecords.remove(0); 246 fastRefresh(); 247 } 248 } 249 250 } 251 252 //-------------------------------------------------------------------------- 253 // Private Methods: 254 //-------------------------------------------------------------------------- 255 private int numberOfRecordsToTrim() { 256 return _allRecords.size() - _maxNumberOfLogRecords; 257 } 258 259 //-------------------------------------------------------------------------- 260 // Nested Top-Level Classes or Interfaces 261 //-------------------------------------------------------------------------- 262} 263