View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.log4j.chainsaw;
18  
19  import java.awt.BorderLayout;
20  import java.text.MessageFormat;
21  import java.util.Date;
22  import javax.swing.BorderFactory;
23  import javax.swing.JEditorPane;
24  import javax.swing.JPanel;
25  import javax.swing.JScrollPane;
26  import javax.swing.JTable;
27  import javax.swing.ListSelectionModel;
28  import javax.swing.event.ListSelectionEvent;
29  import javax.swing.event.ListSelectionListener;
30  import org.apache.log4j.Logger;
31  
32  /**
33   * A panel for showing a stack trace.
34   *
35   * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
36   */
37  class DetailPanel
38      extends JPanel
39      implements ListSelectionListener
40  {
41      /** used to log events **/
42      private static final Logger LOG =
43          Logger.getLogger(DetailPanel.class);
44  
45      /** used to format the logging event **/
46      private static final MessageFormat FORMATTER = new MessageFormat(
47          "<b>Time:</b> <code>{0,time,medium}</code>" +
48          "&nbsp;&nbsp;<b>Priority:</b> <code>{1}</code>" +
49          "&nbsp;&nbsp;<b>Thread:</b> <code>{2}</code>" +
50          "&nbsp;&nbsp;<b>NDC:</b> <code>{3}</code>" +
51          "<br><b>Logger:</b> <code>{4}</code>" +
52          "<br><b>Location:</b> <code>{5}</code>" +
53          "<br><b>Message:</b>" +
54          "<pre>{6}</pre>" +
55          "<b>Throwable:</b>" +
56          "<pre>{7}</pre>");
57  
58      /** the model for the data to render **/
59      private final MyTableModel mModel;
60      /** pane for rendering detail **/
61      private final JEditorPane mDetails;
62  
63      /**
64       * Creates a new <code>DetailPanel</code> instance.
65       *
66       * @param aTable the table to listen for selections on
67       * @param aModel the model backing the table
68       */
69      DetailPanel(JTable aTable, final MyTableModel aModel) {
70          mModel = aModel;
71          setLayout(new BorderLayout());
72          setBorder(BorderFactory.createTitledBorder("Details: "));
73  
74          mDetails = new JEditorPane();
75          mDetails.setEditable(false);
76          mDetails.setContentType("text/html");
77          add(new JScrollPane(mDetails), BorderLayout.CENTER);
78  
79          final ListSelectionModel rowSM = aTable.getSelectionModel();
80          rowSM.addListSelectionListener(this);
81      }
82  
83      /** @see ListSelectionListener **/
84      public void valueChanged(ListSelectionEvent aEvent) {
85          //Ignore extra messages.
86          if (aEvent.getValueIsAdjusting()) {
87              return;
88          }
89  
90          final ListSelectionModel lsm = (ListSelectionModel) aEvent.getSource();
91          if (lsm.isSelectionEmpty()) {
92              mDetails.setText("Nothing selected");
93          } else {
94              final int selectedRow = lsm.getMinSelectionIndex();
95              final EventDetails e = mModel.getEventDetails(selectedRow);
96              final Object[] args =
97              {
98                  new Date(e.getTimeStamp()),
99                  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 &lt;, &gt; &amp; and &quot; as their entities. It is very
138      * dumb about &amp; 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("&lt;");
153                 break;
154             case '>':
155                 buf.append("&gt;");
156                 break;
157             case '\"':
158                 buf.append("&quot;");
159                 break;
160             case '&':
161                 buf.append("&amp;");
162                 break;
163             default:
164                 buf.append(c);
165                 break;
166             }
167         }
168         return buf.toString();
169     }
170 }