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.chainsaw; 018 019import java.awt.BorderLayout; 020import java.text.MessageFormat; 021import java.util.Date; 022import javax.swing.BorderFactory; 023import javax.swing.JEditorPane; 024import javax.swing.JPanel; 025import javax.swing.JScrollPane; 026import javax.swing.JTable; 027import javax.swing.ListSelectionModel; 028import javax.swing.event.ListSelectionEvent; 029import javax.swing.event.ListSelectionListener; 030import org.apache.log4j.Logger; 031 032/** 033 * A panel for showing a stack trace. 034 * 035 * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a> 036 */ 037class DetailPanel 038 extends JPanel 039 implements ListSelectionListener 040{ 041 /** used to log events **/ 042 private static final Logger LOG = 043 Logger.getLogger(DetailPanel.class); 044 045 /** used to format the logging event **/ 046 private static final MessageFormat FORMATTER = new MessageFormat( 047 "<b>Time:</b> <code>{0,time,medium}</code>" + 048 " <b>Priority:</b> <code>{1}</code>" + 049 " <b>Thread:</b> <code>{2}</code>" + 050 " <b>NDC:</b> <code>{3}</code>" + 051 "<br><b>Logger:</b> <code>{4}</code>" + 052 "<br><b>Location:</b> <code>{5}</code>" + 053 "<br><b>Message:</b>" + 054 "<pre>{6}</pre>" + 055 "<b>Throwable:</b>" + 056 "<pre>{7}</pre>"); 057 058 /** the model for the data to render **/ 059 private final MyTableModel mModel; 060 /** pane for rendering detail **/ 061 private final JEditorPane mDetails; 062 063 /** 064 * Creates a new <code>DetailPanel</code> instance. 065 * 066 * @param aTable the table to listen for selections on 067 * @param aModel the model backing the table 068 */ 069 DetailPanel(JTable aTable, final MyTableModel aModel) { 070 mModel = aModel; 071 setLayout(new BorderLayout()); 072 setBorder(BorderFactory.createTitledBorder("Details: ")); 073 074 mDetails = new JEditorPane(); 075 mDetails.setEditable(false); 076 mDetails.setContentType("text/html"); 077 add(new JScrollPane(mDetails), BorderLayout.CENTER); 078 079 final ListSelectionModel rowSM = aTable.getSelectionModel(); 080 rowSM.addListSelectionListener(this); 081 } 082 083 /** @see ListSelectionListener **/ 084 public void valueChanged(ListSelectionEvent aEvent) { 085 //Ignore extra messages. 086 if (aEvent.getValueIsAdjusting()) { 087 return; 088 } 089 090 final ListSelectionModel lsm = (ListSelectionModel) aEvent.getSource(); 091 if (lsm.isSelectionEmpty()) { 092 mDetails.setText("Nothing selected"); 093 } else { 094 final int selectedRow = lsm.getMinSelectionIndex(); 095 final EventDetails e = mModel.getEventDetails(selectedRow); 096 final Object[] args = 097 { 098 new Date(e.getTimeStamp()), 099 e.getPriority(), 100 escape(e.getThreadName()), 101 escape(e.getNDC()), 102 escape(e.getCategoryName()), 103 escape(e.getLocationDetails()), 104 escape(e.getMessage()), 105 escape(getThrowableStrRep(e)) 106 }; 107 mDetails.setText(FORMATTER.format(args)); 108 mDetails.setCaretPosition(0); 109 } 110 } 111 112 //////////////////////////////////////////////////////////////////////////// 113 // Private methods 114 //////////////////////////////////////////////////////////////////////////// 115 116 /** 117 * Returns a string representation of a throwable. 118 * 119 * @param aEvent contains the throwable information 120 * @return a <code>String</code> value 121 */ 122 private static String getThrowableStrRep(EventDetails aEvent) { 123 final String[] strs = aEvent.getThrowableStrRep(); 124 if (strs == null) { 125 return null; 126 } 127 128 final StringBuffer sb = new StringBuffer(); 129 for (int i = 0; i < strs.length; i++) { 130 sb.append(strs[i]).append("\n"); 131 } 132 133 return sb.toString(); 134 } 135 136 /** 137 * Escape <, > & and " as their entities. It is very 138 * dumb about & handling. 139 * @param aStr the String to escape. 140 * @return the escaped String 141 */ 142 private String escape(String aStr) { 143 if (aStr == null) { 144 return null; 145 } 146 147 final StringBuffer buf = new StringBuffer(); 148 for (int i = 0; i < aStr.length(); i++) { 149 char c = aStr.charAt(i); 150 switch (c) { 151 case '<': 152 buf.append("<"); 153 break; 154 case '>': 155 buf.append(">"); 156 break; 157 case '\"': 158 buf.append("""); 159 break; 160 case '&': 161 buf.append("&"); 162 break; 163 default: 164 buf.append(c); 165 break; 166 } 167 } 168 return buf.toString(); 169 } 170}