1 /* 2 * $Id$ 3 * 4 * Copyright (c) 2006, 2009, Oracle and/or its affiliates. All rights reserved. 5 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6 * 7 * This code is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 only, as 9 * published by the Free Software Foundation. Oracle designates this 10 * particular file as subject to the "Classpath" exception as provided 11 * by Oracle in the LICENSE file that accompanied this code. 12 * 13 * This code is distributed in the hope that it will be useful, but WITHOUT 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16 * version 2 for more details (a copy is included in the LICENSE file that 17 * accompanied this code). 18 * 19 * You should have received a copy of the GNU General Public License version 20 * 2 along with this work; if not, write to the Free Software Foundation, 21 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 22 * 23 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 24 * or visit www.oracle.com if you need additional information or have any 25 * questions. 26 */ 27 package com.sun.javatest.exec; 28 29 30 import com.sun.javatest.TestSuite; 31 import com.sun.javatest.WorkDirectory; 32 import com.sun.javatest.logging.FilteredLogModel; 33 import com.sun.javatest.logging.LogModel; 34 import com.sun.javatest.tool.UIFactory; 35 import com.sun.javatest.tool.ToolDialog; 36 import com.sun.javatest.logging.LoggerFactory; 37 import com.sun.javatest.tool.FileChooser; 38 import com.sun.javatest.tool.Preferences; 39 40 import java.awt.*; 41 import java.awt.event.ActionEvent; 42 import java.awt.event.ActionListener; 43 import java.awt.event.ItemEvent; 44 import java.awt.event.ItemListener; 45 import java.io.File; 46 import java.io.IOException; 47 import java.util.ArrayList; 48 import java.util.EventObject; 49 import java.util.Random; 50 import java.util.HashSet; 51 import java.util.logging.Level; 52 import java.util.logging.Logger; 53 import javax.swing.AbstractAction; 54 import javax.swing.DefaultListCellRenderer; 55 import javax.swing.JButton; 56 import javax.swing.JCheckBox; 57 import javax.swing.JComboBox; 58 import javax.swing.JComponent; 59 import javax.swing.JDialog; 60 import javax.swing.JFileChooser; 61 import javax.swing.JLabel; 62 import javax.swing.JList; 63 import javax.swing.JOptionPane; 64 import javax.swing.JPanel; 65 import javax.swing.JScrollBar; 66 import javax.swing.JScrollPane; 67 import javax.swing.JSeparator; 68 import javax.swing.JTextField; 69 import javax.swing.JTextPane; 70 import javax.swing.JTree; 71 import javax.swing.ListCellRenderer; 72 import javax.swing.SwingConstants; 73 import javax.swing.SwingUtilities; 74 import javax.swing.text.AbstractDocument; 75 import javax.swing.text.BadLocationException; 76 import javax.swing.text.BoxView; 77 import javax.swing.text.ComponentView; 78 import javax.swing.text.Element; 79 import javax.swing.text.IconView; 80 import javax.swing.text.LabelView; 81 import javax.swing.text.ParagraphView; 82 import javax.swing.text.Style; 83 import javax.swing.text.StyleConstants; 84 import javax.swing.text.StyleContext; 85 import javax.swing.text.StyledDocument; 86 import javax.swing.text.StyledEditorKit; 87 import javax.swing.text.View; 88 import javax.swing.text.ViewFactory; 89 import javax.swing.tree.DefaultMutableTreeNode; 90 import javax.swing.tree.DefaultTreeCellEditor; 91 import javax.swing.tree.DefaultTreeCellRenderer; 92 import javax.swing.tree.TreeCellRenderer; 93 import javax.swing.tree.DefaultTreeModel; 94 import javax.swing.tree.TreePath; 95 96 class LogViewer extends ToolDialog { 97 98 public LogViewer(WorkDirectory workDir, UIFactory uif, Component parent) { 99 super(parent, uif, "logviewer"); 100 String fileName = workDir.getLogFileName(); 101 this.uif = uif; 102 this.workDir = workDir; 103 makeLogger(workDir); 104 if (debug > 1 && log != null) { 105 log.info("New LogViewer started"); 106 } 107 model = new FilteredLogModel(workDir.getTestSuite().getObservedFile(fileName), fileName); 108 model.setLogger(log); 109 110 initGUI(); 111 112 model.addNewLoggerListener(new LogModel.LoggerListener() { 113 public void onNewLogger(String logName) { 114 if (debug > 1) { 115 String text = "Loggers : " + model.getLoggers().size(); 116 loggerCounter.setText(text); 117 } 118 } 119 120 121 public void onRemoveAllLoggers() { 122 if (debug > 1) { 123 String text = "Loggers : " + model.getLoggers().size(); 124 loggerCounter.setText(text); 125 } 126 } 127 128 }); 129 130 model.addNewPageListener(new LogModel.NewPageListener() { 131 public void onNewPage(final int from, final int to, final int page) { 132 synchronized (thePane) { 133 134 if (debugPages > 1) { 135 System.out.println("isStable=" + model.isStableState() + " onNewPage from=" + from + " to=" + to + " page=" + page + " thePane.page=" + thePane.page); 136 String text = "Records : " + model.recordsRead(); 137 counter.setText(text); 138 text = "Pages : " + model.pagesRead(); 139 pageCounter.setText(text); 140 } 141 142 if (thePane.page == 0 && model.isStableState()) { 143 setPage(page); 144 } else if (thePane.page == page && model.isStableState()){ 145 updatePage(from, to); 146 } else if (model.isStableState()) { 147 updateNavBtns(); 148 } 149 } 150 } 151 }); 152 153 model.addFilterChangedListener(new FilteredLogModel.FilterChangedListener() { 154 public void onFilterChanged() { 155 synchronized (thePane) { 156 thePane.page = 0; 157 thePane.fromRec = 0; 158 } 159 } 160 }); 161 162 model.init(); 163 updateNavBtns(); 164 setVisible(true); 165 166 setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); 167 168 LV_Scroller autoScroller = new LV_Scroller(); 169 autoScroller.start(); 170 } 171 172 protected void windowClosingAction(AWTEvent e) { 173 onClose(null); 174 } 175 176 private void makeLogger(final WorkDirectory workDir) { 177 log = Logger.getLogger("LogViewer"); 178 try { 179 log = workDir.getTestSuite().createLog(workDir, null, "LogViewer"); 180 } catch (TestSuite.DuplicateLogNameFault ex) { 181 try { 182 log = workDir.getTestSuite().getLog(workDir, "LogViewer"); 183 } catch (TestSuite.NoSuchLogFault exe) { 184 exe.printStackTrace(); 185 } 186 } 187 } 188 189 protected void initGUI() { 190 191 working1 = uif.getI18NString("logviewer.working1"); 192 working2 = uif.getI18NString("logviewer.working2"); 193 194 JPanel body = new JPanel(); 195 addWindowToList(); 196 setI18NTitle("logviewer.title", windowCounter); 197 198 GridBagConstraints gridBagConstraints; 199 200 JPanel filterPanel = new JPanel(); 201 filterPanel.setLayout(new GridBagLayout()); 202 203 Object[] items = { 204 uif.getI18NString("logviewer.combobox.actions"), 205 new JSeparator(), 206 new FilterComboboxItem(uif.getI18NString("logviewer.combobox.selectall"), true), 207 new FilterComboboxItem(uif.getI18NString("logviewer.combobox.clearall"), false), 208 new JSeparator(), 209 new FilterComboboxItem(uif.getI18NString("logviewer.combobox.select") + levelNames[0], levels[0]), 210 new FilterComboboxItem(uif.getI18NString("logviewer.combobox.select") + levelNames[1], levels[1]), 211 new FilterComboboxItem(uif.getI18NString("logviewer.combobox.select") + levelNames[2], levels[2]), 212 new FilterComboboxItem(uif.getI18NString("logviewer.combobox.select") + levelNames[3], levels[3]) 213 }; 214 215 filterCombo = uif.createLiteralChoice("logviewer.combobox", items); //new JComboBox(items); 216 uif.setAccessibleName(filterCombo, filterCombo.getName()); 217 filterCombo.setRenderer(new CustomRenderer()); 218 filterTreeScroll = new JScrollPane(); 219 220 createFilterTree(filterTreeScroll); 221 222 JLabel filterSubstringLbl = uif.createLabel("logviewer.label.find"); 223 final JTextField filterSubstring = uif.createInputField("logviewer.fitertext"); 224 uif.setAccessibleInfo(filterSubstring, filterSubstring.getName()); 225 filterSubstring.addActionListener(new ActionListener() { 226 public void actionPerformed(ActionEvent e) { 227 model.getFilter().setSubstring(filterSubstring.getText()); 228 } 229 }); 230 231 autoScrollCheckBox = uif.createCheckBox("logviewer.autoscroll"); 232 autoScroll = Boolean.parseBoolean(prefs.getPreference(AUTOSCROLL_PREF, Boolean.toString(true))); 233 autoScrollCheckBox.setSelected(autoScroll); 234 autoScrollCheckBox.addItemListener(new ItemListener() { 235 public void itemStateChanged(ItemEvent e) { 236 autoScroll = (e.getStateChange() == ItemEvent.SELECTED); 237 prefs.setPreference(AUTOSCROLL_PREF, Boolean.toString(autoScroll)); 238 } 239 }); 240 241 JCheckBox wordWrapCheckBox = uif.createCheckBox("logviewer.wordwarp"); 242 wordWrap = Boolean.parseBoolean(prefs.getPreference(WORDWRAP_PREF, Boolean.toString(false))); 243 wordWrapCheckBox.setSelected(wordWrap); 244 wordWrapCheckBox.addItemListener(new ItemListener() { 245 public void itemStateChanged(ItemEvent e) { 246 wordWrap = (e.getStateChange() == ItemEvent.SELECTED); 247 synchronized (thePane) { 248 249 // try to restore position (not exactly) - 250 Point vp = scrollPane.getViewport().getViewPosition(); 251 Dimension vs = scrollPane.getViewport().getViewSize(); 252 final double magic = vp.getY()/vs.getHeight(); 253 254 int p = thePane.page; 255 clearPane(0); 256 setPage(p); 257 SwingUtilities.invokeLater(new Runnable() { 258 public void run() { 259 double newMagic = thePane.getDocument().getLength()*magic; 260 thePane.setCaretPosition((int)newMagic); 261 } 262 }); 263 } 264 prefs.setPreference(WORDWRAP_PREF, Boolean.toString(wordWrap)); 265 } 266 }); 267 268 scrollPane = new JScrollPane(); 269 thePane = new LogPane(); 270 271 272 thePane.setEditorKit(new LogEditorKit()); 273 JButton btnClose = uif.createButton("logviewer.button.close"); 274 JButton btnNew = uif.createButton("logviewer.button.open"); 275 btnSave = uif.createButton("logviewer.button.save"); 276 btnClear = uif.createButton("logviewer.button.clear"); 277 if (debug != 0 || debugPages != 0) { 278 counter = new JLabel(); 279 pageCounter = new JLabel(); 280 loggerCounter = new JLabel(); 281 currPage = new JLabel(); 282 } 283 naviPanel = new JPanel(); 284 processLabel = new JLabel(); 285 Font old = processLabel.getFont(); 286 processLabel.setFont(old.deriveFont(old.getSize()-1)); 287 JPanel naviBtnPanel = new JPanel(); 288 lblPageCounter = new JLabel(); 289 lblPageCounter.setVerticalAlignment(SwingConstants.TOP); 290 btnFirst = uif.createButton("logviewer.firstpage"); 291 btnNext = uif.createButton("logviewer.nextpage"); 292 btnPrev = uif.createButton("logviewer.previouspage"); 293 btnLast = uif.createButton("logviewer.lastspage"); 294 295 body.setLayout(new GridBagLayout()); 296 297 thePane.setEditable(false); 298 thePane.setPreferredSize(new Dimension(600, 400)); 299 scrollPane.setViewportView(thePane); 300 301 gridBagConstraints = new GridBagConstraints(); 302 gridBagConstraints.insets = new Insets(0, 0, 5, 0); 303 filterPanel.add(filterCombo, gridBagConstraints); 304 305 gridBagConstraints = new java.awt.GridBagConstraints(); 306 gridBagConstraints.gridx = 0; 307 gridBagConstraints.gridy = 1; 308 gridBagConstraints.weighty = 1.0; 309 gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; 310 filterPanel.add(filterTreeScroll, gridBagConstraints); 311 312 gridBagConstraints = new GridBagConstraints(); 313 gridBagConstraints.gridx = 0; 314 gridBagConstraints.gridy = 2; 315 gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; 316 gridBagConstraints.insets = new Insets(5, 0, 0, 0); 317 filterPanel.add(filterSubstringLbl, gridBagConstraints); 318 319 gridBagConstraints = new GridBagConstraints(); 320 gridBagConstraints.gridx = 0; 321 gridBagConstraints.gridy = 3; 322 gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; 323 filterPanel.add(filterSubstring, gridBagConstraints); 324 325 gridBagConstraints = new GridBagConstraints(); 326 gridBagConstraints.gridx = 0; 327 gridBagConstraints.gridy = 0; 328 gridBagConstraints.fill = GridBagConstraints.BOTH; 329 gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; 330 gridBagConstraints.weighty = 1.0; 331 gridBagConstraints.insets = new Insets(11, 11, 5, 5); 332 333 body.add(filterPanel, gridBagConstraints); 334 335 initPane(); 336 337 gridBagConstraints = new GridBagConstraints(); 338 gridBagConstraints.gridx = 1; 339 gridBagConstraints.gridy = 0; 340 gridBagConstraints.fill = GridBagConstraints.BOTH; 341 gridBagConstraints.weightx = 1.0; 342 gridBagConstraints.weighty = 1.0; 343 gridBagConstraints.insets = new Insets(11, 5, 5, 11); 344 body.add(scrollPane, gridBagConstraints); 345 346 btnClose.addActionListener(new ActionListener() { 347 public void actionPerformed(ActionEvent evt) { 348 onClose(evt); 349 } 350 }); 351 352 btnNew.addActionListener(new ActionListener() { 353 public void actionPerformed(ActionEvent evt) { 354 onNew(evt); 355 } 356 }); 357 358 btnSave.addActionListener(new ActionListener() { 359 public void actionPerformed(ActionEvent evt) { 360 onSave(evt); 361 } 362 }); 363 364 btnClear.addActionListener(new ActionListener() { 365 public void actionPerformed(ActionEvent evt) { 366 onClear(evt); 367 } 368 }); 369 370 371 gridBagConstraints = new GridBagConstraints(); 372 gridBagConstraints.gridx = 0; 373 gridBagConstraints.gridy = 1; 374 gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; 375 gridBagConstraints.insets = new Insets(0, 11, 0, 0); 376 body.add(autoScrollCheckBox, gridBagConstraints); 377 378 gridBagConstraints = new GridBagConstraints(); 379 gridBagConstraints.gridx = 0; 380 gridBagConstraints.gridy = 2; 381 gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; 382 gridBagConstraints.insets = new Insets(0, 11, 0, 0); 383 body.add(wordWrapCheckBox, gridBagConstraints); 384 385 if (debug != 0) { 386 387 counter.setText("Records : "); 388 gridBagConstraints = new GridBagConstraints(); 389 gridBagConstraints.gridx = 0; 390 gridBagConstraints.gridy = 3; 391 gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; 392 gridBagConstraints.insets = new Insets(0, 5, 0, 5); 393 body.add(counter, gridBagConstraints); 394 395 pageCounter.setText("Pages : "); 396 gridBagConstraints = new GridBagConstraints(); 397 gridBagConstraints.gridx = 0; 398 gridBagConstraints.gridy = 4; 399 gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; 400 gridBagConstraints.insets = new Insets(0, 5, 0, 5); 401 body.add(pageCounter, gridBagConstraints); 402 403 loggerCounter.setText("Loggers : "); 404 gridBagConstraints = new GridBagConstraints(); 405 gridBagConstraints.gridx = 0; 406 gridBagConstraints.gridy = 5; 407 gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; 408 gridBagConstraints.insets = new Insets(0, 5, 0, 5); 409 body.add(loggerCounter, gridBagConstraints); 410 411 currPage.setText("Current page "); 412 gridBagConstraints = new GridBagConstraints(); 413 gridBagConstraints.gridx = 0; 414 gridBagConstraints.gridy = 6; 415 gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; 416 gridBagConstraints.insets = new Insets(0, 5, 5, 5); 417 body.add(currPage, gridBagConstraints); 418 419 JButton btnGen = new JButton(new AbstractAction("Generate !") { 420 public void actionPerformed(ActionEvent e) { 421 log.config("Config"); 422 } 423 }); 424 JButton btnGen2 = new JButton(new AbstractAction("Generate 2") { 425 public void actionPerformed(ActionEvent e) { 426 for (int i = 0; i < 10000; i++) { 427 Random r = new Random(); 428 Logger myLog = getLogger(); 429 myLog.log(Level.FINE, "To be, or not to be: that is the question:\n"+ 430 "Whether 'tis nobler in the mind to suffer\n"+ 431 "The slings and arrows of outrageous fortune,\n"+ 432 "Or to take arms against a sea of troubles,\n"+ 433 "And by opposing end them? To die: to sleep;"); 434 myLog.log(Level.INFO, "Random long " + r.nextLong()); 435 myLog.log(Level.WARNING, "The World Health Organization says a cluster of bird flu cases in Indonesia may have been caused by human-to-human transmission. \nAn outbreak of bird flu that infected at least seven Indonesian family members earlier this month in north Sumatra was not a mutated version of the often deadly H5N1 form of the virus, World Health Organization spokesman Peter Cordingly told CNN."); 436 myLog.config("Config"); 437 } 438 } 439 }); 440 441 gridBagConstraints = new GridBagConstraints(); 442 gridBagConstraints.gridx = 1; 443 gridBagConstraints.gridy = 3; 444 body.add(btnGen, gridBagConstraints); 445 gridBagConstraints.gridy = 4; 446 body.add(btnGen2, gridBagConstraints); 447 } 448 449 naviBtnPanel.setLayout(new GridLayout(1, 4, 10, 10)); 450 btnFirst.setEnabled(false); 451 btnFirst.addActionListener(new ActionListener() { 452 public void actionPerformed(ActionEvent evt) { 453 goFirst(evt); 454 } 455 }); 456 457 naviBtnPanel.add(btnFirst); 458 459 btnPrev.setEnabled(false); 460 btnPrev.addActionListener(new ActionListener() { 461 public void actionPerformed(ActionEvent evt) { 462 goPrev(evt); 463 } 464 }); 465 466 naviBtnPanel.add(btnPrev); 467 468 btnNext.setEnabled(false); 469 btnNext.addActionListener(new ActionListener() { 470 public void actionPerformed(ActionEvent evt) { 471 goNext(evt); 472 } 473 }); 474 475 naviBtnPanel.add(btnNext); 476 477 btnLast.setEnabled(false); 478 btnLast.addActionListener(new ActionListener() { 479 public void actionPerformed(ActionEvent evt) { 480 goLast(evt); 481 } 482 }); 483 484 naviBtnPanel.add(btnLast); 485 486 naviPanel.setLayout(new BorderLayout()); 487 naviPanel.add(lblPageCounter, BorderLayout.WEST); 488 naviPanel.add(naviBtnPanel, BorderLayout.EAST); 489 490 491 gridBagConstraints = new GridBagConstraints(); 492 gridBagConstraints.gridx = 1; 493 gridBagConstraints.gridy = 1; 494 gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; 495 gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; 496 gridBagConstraints.weightx = 1.0; 497 gridBagConstraints.gridheight = 2; 498 gridBagConstraints.insets = new Insets(0, 5, 0, 0); 499 body.add(processLabel, gridBagConstraints); 500 501 502 gridBagConstraints = new GridBagConstraints(); 503 gridBagConstraints.gridx = 1; 504 gridBagConstraints.gridy = 2; 505 gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; 506 gridBagConstraints.anchor = GridBagConstraints.NORTHEAST; 507 gridBagConstraints.weightx = 1.0; 508 gridBagConstraints.gridheight = 2; 509 gridBagConstraints.insets = new Insets(0, 5, 5, 11); 510 body.add(naviPanel, gridBagConstraints); 511 512 setButtons(new JButton[] {btnClear, btnSave, btnNew, btnClose}, btnClose); 513 514 setBody(body); 515 } 516 517 private void createFilterTree(final JScrollPane filterTreeScroll) { 518 treeRoot = new DefaultMutableTreeNode(new JCheckBox("Root")); 519 filterTree = new JTree(); 520 LoggersTreeModel ltm = new LoggersTreeModel(); 521 filterTree.setModel(ltm); 522 filterTree.setCellRenderer(new CheckBoxRenderer()); 523 filterTree.setCellEditor(new CheckBoxEditor(filterTree)); 524 filterTree.setRootVisible(false); 525 filterTree.setEditable(true); 526 filterTreeScroll.setViewportView(filterTree); 527 if (filterComboBoxListener != null) { 528 filterCombo.removeActionListener(filterComboBoxListener); 529 } 530 filterComboBoxListener = new FilterComboBoxListener(ltm); 531 filterCombo.addActionListener(filterComboBoxListener); 532 filterTree.setName("logviewer.logtree"); 533 uif.setAccessibleInfo(filterTree, filterTree.getName()); 534 } 535 536 private Logger getLogger() { 537 Random r = new Random(); 538 String lName = "log_" + r.nextInt(10); 539 Logger ret = null; 540 541 try { 542 ret = workDir.getTestSuite().createLog(workDir, null, lName); 543 } catch (TestSuite.DuplicateLogNameFault ex) { 544 try { 545 ret = workDir.getTestSuite().getLog(workDir, lName); 546 } catch (TestSuite.NoSuchLogFault exe) { 547 exe.printStackTrace(); 548 } 549 } 550 return ret; 551 } 552 553 private class EditorFiller extends Thread { 554 EditorFiller(int from, int to, int pagenum) { 555 super("editorFiller"); 556 this.from=from; 557 this.to=to; 558 this.pagenum=pagenum; 559 } 560 561 public void run() { 562 if (noWindow) return; 563 if (Thread.currentThread().isInterrupted()) { 564 return; 565 } 566 ArrayList<LogModel.LiteLogRecord> records = model.getRecords(); 567 for (int i = from; i <= to && i < records.size() && i >= 0; i++) { 568 if (noWindow) return; 569 LogModel.LiteLogRecord rec = records.get(i); 570 if (rec == null) { 571 continue; 572 } 573 if (Thread.currentThread().isInterrupted()) { 574 return; 575 } 576 final String msg = model.getRecordMessage(rec); 577 final int level = rec.severety; 578 String hd = rec.getHeader(model.getLogname(rec.loggerID)); 579 final String header = debug > 1 ? "#" + (i+1) + " " + hd : hd; 580 final String substr = model.getFilter().getSubstring(); 581 final int iter = i; 582 SwingUtilities.invokeLater(new Runnable() { 583 public void run() { 584 if (noWindow) return; 585 synchronized (thePane) { 586 if (iter <= thePane.fromRec) return; 587 thePane.page = pagenum; 588 thePane.fromRec = iter; 589 } 590 try { 591 int pos = doc.getEndPosition().getOffset()-1; 592 if (thePane.getCaret() == null) { 593 thePane.setCaretPosition(0); 594 } 595 int oldPos = thePane.getCaretPosition(); 596 doc.insertString(pos, header, getStyle(level)); 597 doc.insertString(doc.getEndPosition().getOffset()-1, "\n", getStyle(level)); 598 599 if (!"".equals(substr)) { 600 String up = header.toUpperCase(); 601 int s = 0; int ss; 602 while ((ss = up.indexOf(substr, s)) >= 0) { 603 doc.setCharacterAttributes(pos + ss, substr.length(), selected, false); 604 s += substr.length(); 605 } 606 } 607 pos = doc.getEndPosition().getOffset()-1; 608 doc.insertString(pos, msg, styleMsg); 609 doc.insertString(doc.getEndPosition().getOffset()-1, "\n", getStyle(level)); 610 611 if (!"".equals(substr)) { 612 String up = msg.toUpperCase(); 613 int s = 0; int ss; 614 while ((ss = up.indexOf(substr, s)) >= 0) { 615 doc.setCharacterAttributes(pos + ss, substr.length(), selected, false); 616 s += substr.length(); 617 } 618 } 619 thePane.setCaretPosition(oldPos); 620 if (noWindow) return; 621 } catch (BadLocationException ex) { 622 ex.printStackTrace(); 623 } 624 } 625 } ); 626 } 627 } 628 629 630 private int pagenum; 631 private int to; 632 private int from; 633 } 634 635 636 private void setRecords(final int from, final int to, final int pagenum) { 637 if (editorThread != null && editorThread.isAlive()) { 638 editorThread.interrupt(); 639 try { 640 editorThread.join(); 641 } catch (InterruptedException ex) { 642 // it's ok 643 } 644 } 645 editorThread = new EditorFiller(from, to, pagenum); 646 editorThread.setPriority(Thread.MIN_PRIORITY); 647 editorThread.start(); 648 } 649 650 651 private void clearPane(final int from) { 652 SwingUtilities.invokeLater(new Runnable() { 653 public void run() { 654 if (doc != null) { 655 try { 656 doc.remove(0, doc.getEndPosition().getOffset()-1); 657 synchronized (thePane) { 658 thePane.fromRec = from-1; 659 } 660 } catch (BadLocationException ex) { 661 ex.printStackTrace(); 662 } 663 } 664 } 665 } ); 666 } 667 668 669 private Style getStyle(int level) { 670 if (level < Level.INFO.intValue()) { 671 return styleOther; 672 } else if (level < Level.WARNING.intValue()) { 673 return styleInfo; 674 } else if (level < Level.SEVERE.intValue()) { 675 return styleWarning; 676 } else { 677 return styleSevere; 678 } 679 } 680 681 682 private void goLast(ActionEvent evt) { 683 // it's important to put it to the end of the queue ! 684 SwingUtilities.invokeLater(new Runnable() { 685 public void run() { 686 synchronized (thePane) { 687 setPage(model.pagesRead()); 688 } 689 } 690 }); 691 } 692 693 private void goPrev(ActionEvent evt) { 694 // it's important to put it to the end of the queue ! 695 SwingUtilities.invokeLater(new Runnable() { 696 public void run() { 697 synchronized (thePane) { 698 autoScrollCheckBox.setSelected(false); 699 autoScroll = false; 700 setPage(thePane.page-1); 701 } 702 } 703 }); 704 } 705 706 private void goNext(ActionEvent evt) { 707 // it's important to put it to the end of the queue ! 708 SwingUtilities.invokeLater(new Runnable() { 709 public void run() { 710 synchronized (thePane) { 711 setPage(thePane.page+1); 712 } 713 } 714 }); 715 } 716 717 private void goFirst(ActionEvent evt) { 718 // it's important to put it to the end of the queue ! 719 SwingUtilities.invokeLater(new Runnable() { 720 public void run() { 721 synchronized (thePane) { 722 autoScrollCheckBox.setSelected(false); 723 autoScroll = false; 724 setPage(1); 725 } 726 } 727 }); 728 } 729 730 private void onClose(ActionEvent evt) { 731 dispose(); 732 } 733 734 private void onNew(ActionEvent evt) { 735 LogViewer lv = new LogViewer(workDir, uif, parent); 736 Point newL = getLocation(); 737 newL.translate(20, 20); 738 lv.setLocation(newL); 739 } 740 741 private void onSave(ActionEvent evt) { 742 FileChooser fileChooser = new FileChooser(true); 743 fileChooser.addChoosableExtension(".xml", 744 uif.getI18NString("logviewer.save.ext.xml")); 745 fileChooser.setDialogTitle(uif.getI18NString("logviewer.save.title")); 746 if (fileChooser.showDialog(parent, uif.getI18NString("ce.save.btn")) != JFileChooser.APPROVE_OPTION) { 747 // user has canceled or closed the chooser 748 return; 749 } 750 File f = fileChooser.getSelectedFile(); 751 if (f != null) { 752 753 if (!f.getName().toLowerCase().endsWith(".xml")) { 754 f = new File(f.getPath() + ".xml"); 755 } 756 757 LogViewerTools rm = new LogViewerTools(model, f, log, parent, uif); 758 rm.go(); 759 } 760 } 761 762 private void onClear(ActionEvent evt) { 763 if (uif.showYesNoDialog("logviewer.clearconfirm") == JOptionPane.YES_OPTION) { 764 try { 765 workDir.getTestSuite().eraseLog(workDir); 766 } catch (IOException ex) { 767 ex.printStackTrace(); 768 } 769 } 770 } 771 772 private void updatePage(int from, int to) { 773 setRecords(from, to, thePane.page); 774 updateNavBtns(); 775 } 776 777 private void setPage(int pageNum) { 778 int from, to; 779 from = (pageNum - 1) * model.getPageSize() ; 780 to = pageNum * model.getPageSize() - 1; 781 782 synchronized (thePane) { 783 thePane.page = pageNum; 784 } 785 786 clearPane(from); 787 setRecords(from, to, pageNum); 788 789 updateNavBtns(); 790 791 if (debugPages > 1) { 792 System.out.println("setPage " + pageNum + " thePane.page=" + thePane.page ); 793 SwingUtilities.invokeLater(new Runnable() { 794 public void run() { 795 if (thePane != null && currPage != null) { 796 currPage.setText("Current page " + thePane.page ); 797 } 798 } 799 }); 800 } 801 } 802 803 private void updateNavBtns() { 804 // can be called not from swing thread 805 SwingUtilities.invokeLater(new Runnable(){ 806 public void run() { 807 if (noWindow) return; 808 synchronized (thePane) { 809 if (model != null && btnFirst != null && btnLast != null && 810 btnPrev != null && btnNext != null && lblPageCounter != null && 811 naviPanel != null && btnSave != null) { 812 btnFirst.setEnabled(thePane.page > 1); 813 btnLast.setEnabled(thePane.page < model.pagesRead() && model.pagesRead() > 1); 814 btnPrev.setEnabled(thePane.page > 1); 815 btnNext.setEnabled(thePane.page < model.pagesRead() && model.pagesRead() > 1); 816 String pop = uif.getI18NString("logviewer.pageofpage", 817 new Object[] {thePane.page, model.pagesRead()}); 818 lblPageCounter.setText(pop); 819 naviPanel.setVisible(btnFirst.isEnabled() || 820 btnFirst.isEnabled() || 821 btnLast.isEnabled() || 822 btnNext.isEnabled()); 823 btnSave.setEnabled(model.isStableState() && model.recordsRead() > 0); 824 btnClear.setEnabled(model.pagesRead() > 0); 825 } 826 } 827 } 828 }); 829 } 830 831 private void initPane() { 832 doc = thePane.getStyledDocument(); 833 Style def = StyleContext.getDefaultStyleContext(). 834 getStyle(StyleContext.DEFAULT_STYLE); 835 836 styleMsg = doc.addStyle("msg_text", def); 837 StyleConstants.setFontFamily(styleMsg, "Monospaced"); 838 839 styleWait = doc.addStyle("blink", styleMsg); 840 StyleConstants.setBold(styleWait, true); 841 842 styleInfo = doc.addStyle("info_text", styleMsg); 843 Color darkGreen = new Color(0, 180, 0); 844 StyleConstants.setForeground(styleInfo, darkGreen); 845 846 styleWarning = doc.addStyle("warning_text", styleMsg); 847 StyleConstants.setForeground(styleWarning, new Color(200, 150, 0)); 848 849 styleSevere = doc.addStyle("severe_text", styleMsg); 850 StyleConstants.setForeground(styleSevere, Color.RED); 851 852 styleOther = doc.addStyle("other_text", styleMsg); 853 StyleConstants.setForeground(styleOther, Color.BLUE); 854 855 selected = doc.addStyle("selected", null); 856 StyleConstants.setBackground(selected, Color.YELLOW); 857 858 } 859 860 861 private class LogEditorKit extends StyledEditorKit { 862 863 864 public ViewFactory getViewFactory() { 865 if (fact == null) { 866 fact = new LogViewFactory(); 867 } 868 return fact; 869 } 870 ViewFactory fact; 871 872 class NoWrapLabelView extends LabelView { 873 public NoWrapLabelView(Element elem) { 874 super(elem); 875 } 876 877 public int getBreakWeight(int axis, float pos, float len) { 878 return BadBreakWeight; 879 } 880 } 881 882 private class LogViewFactory implements ViewFactory { 883 public View create(Element elem) { 884 String kind = elem.getName(); 885 if (kind != null) { 886 if (kind.equals(AbstractDocument.ContentElementName)) { 887 if (!wordWrap) { 888 return new NoWrapLabelView(elem)/* LabelView(elem)*/; 889 } else { 890 return new LabelView(elem); 891 } 892 } else if (kind.equals(AbstractDocument.ParagraphElementName)) { 893 return new ParagraphView(elem); 894 } else if (kind.equals(AbstractDocument.SectionElementName)) { 895 return new BoxView(elem, View.Y_AXIS); 896 } else if (kind.equals(StyleConstants.ComponentElementName)) { 897 return new ComponentView(elem); 898 } else if (kind.equals(StyleConstants.IconElementName)) { 899 return new IconView(elem); 900 } 901 } 902 903 // default to text display 904 return new LabelView(elem); 905 } 906 } 907 908 } 909 910 private class LogPane extends JTextPane { 911 int page; 912 int fromRec; 913 public LogPane() { 914 super(); 915 setName("logviewer.viewerpane"); 916 uif.setAccessibleName(this, getName()); 917 918 } 919 } 920 921 private class CheckBoxEditor extends DefaultTreeCellEditor { 922 CheckBoxEditor(JTree tree) { 923 super(tree, new DefaultTreeCellRenderer()); 924 } 925 926 public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) { 927 if (value instanceof DefaultMutableTreeNode) { 928 DefaultMutableTreeNode node = (DefaultMutableTreeNode) value; 929 Object o = node.getUserObject(); 930 if (o instanceof JCheckBox) { 931 JCheckBox cb = (JCheckBox) o; 932 cb.setBackground(tree.getBackground()); 933 return cb; 934 } 935 } 936 937 return super.getTreeCellEditorComponent(tree, value, isSelected, expanded, leaf, row); 938 } 939 940 // no expand/collaps !! 941 // edit this to change 942 public boolean isCellEditable(EventObject event) { 943 return true; 944 } 945 946 } 947 948 private class CheckBoxRenderer extends JCheckBox 949 implements TreeCellRenderer { 950 951 public CheckBoxRenderer() { 952 super(); 953 setBackground(filterTree.getBackground()); 954 } 955 956 public Component getTreeCellRendererComponent(JTree tree, 957 Object value, boolean isSelected, boolean expanded, 958 boolean leaf, int row, boolean hasFocus) { 959 960 if (value instanceof DefaultMutableTreeNode) { 961 DefaultMutableTreeNode node = (DefaultMutableTreeNode) value; 962 Object o = node.getUserObject(); 963 if (o instanceof JCheckBox) { 964 JCheckBox cb = (JCheckBox) o; 965 setText(cb.getText()); 966 setSelected(cb.isSelected()); 967 return this; 968 } 969 } 970 971 return defRend.getTreeCellRendererComponent(tree, 972 value, isSelected, expanded, leaf, row, hasFocus); 973 } 974 DefaultTreeCellRenderer defRend = new DefaultTreeCellRenderer(); 975 } 976 977 978 private class CustomRenderer extends JComponent 979 implements ListCellRenderer<Object> { 980 public Component getListCellRendererComponent( 981 JList list, Object value, int index, boolean isSelected, 982 boolean cellHasFocus) { 983 if (!(value instanceof JSeparator) && !(value instanceof JCheckBox)) { 984 DefaultListCellRenderer defRend = new DefaultListCellRenderer(); 985 return defRend.getListCellRendererComponent(list, value, index, 986 isSelected, cellHasFocus); 987 } else if (value instanceof JSeparator) { 988 return (JSeparator)value; 989 } else if (value instanceof JCheckBox) { 990 if (isSelected) { 991 ((JCheckBox)value).setBackground(list.getSelectionBackground()); 992 ((JCheckBox)value).setForeground(list.getSelectionForeground()); 993 } else { 994 ((JCheckBox)value).setBackground(list.getBackground()); 995 ((JCheckBox)value).setForeground(list.getForeground()); 996 } 997 998 return (JCheckBox)value; 999 } 1000 return this; 1001 } 1002 } 1003 1004 private class LoggersTreeModel extends DefaultTreeModel { 1005 LoggersTreeModel() { 1006 super(treeRoot); 1007 model.removeNewLoggerListeners(); 1008 model.addNewLoggerListener(new LogModel.LoggerListener() { 1009 public void onNewLogger(final String name) { 1010 // it calls from Worker thread 1011 SwingUtilities.invokeLater(new Runnable() { 1012 public void run() { 1013 // check for the same 1014 1015 final PropagatedCheckBox ch = new PropagatedCheckBox(name); 1016 ch.setSelected(true); 1017 DefaultMutableTreeNode newLogger = new DefaultMutableTreeNode(ch, true); 1018 1019 for (int i = 0 ; i < levels.length; i++) { 1020 final JCheckBox chh = new FilterTreeItem(name, levels[i].intValue(), levelNames[i]); 1021 chh.setSelected(true); 1022 final int level = levels[i].intValue(); 1023 chh.addItemListener( new ItemListener() { 1024 { 1025 box = chh; 1026 l = level; 1027 logName = name; 1028 } 1029 public void itemStateChanged(ItemEvent e) { 1030 model.getFilter().enableLogger(logName, l, box.isSelected()); 1031 } 1032 JCheckBox box; 1033 int l; 1034 String logName; 1035 }); 1036 ch.addChild(chh); 1037 DefaultMutableTreeNode node = new DefaultMutableTreeNode(chh); 1038 newLogger.add(node); 1039 } 1040 1041 treeRoot.add(newLogger); 1042 nodesWereInserted(treeRoot, new int [] {treeRoot.getIndex(newLogger)}); 1043 filterTree.expandPath(new TreePath(newLogger.getPath())); 1044 1045 } 1046 }); 1047 1048 } 1049 1050 public void onRemoveAllLoggers() { 1051 createFilterTree(filterTreeScroll); 1052 } 1053 }); 1054 } 1055 } 1056 1057 private class PropagatedCheckBox extends JCheckBox { 1058 PropagatedCheckBox(final String name) { 1059 super(name); 1060 this.addItemListener( new ItemListener() { 1061 public void itemStateChanged(ItemEvent e) { 1062 for (JCheckBox aChildren : children) { 1063 boolean s = isSelected(); 1064 if (aChildren.isSelected() != s) { 1065 aChildren.setSelected(s); 1066 if (debug > 1) { 1067 System.out.println(aChildren.getText() + " selected " + s); 1068 } 1069 } 1070 } 1071 filterTree.repaint(); 1072 } 1073 }); 1074 } 1075 1076 public void fireEvent() { 1077 fireItemStateChanged( 1078 new ItemEvent(this, 1079 ItemEvent.ITEM_STATE_CHANGED, 1080 this, 1081 this.isSelected() ? ItemEvent.SELECTED : ItemEvent.DESELECTED)); 1082 } 1083 1084 public void addChild(JCheckBox ch) { 1085 children.add(ch); 1086 } 1087 1088 private ArrayList<JCheckBox> children = new ArrayList<JCheckBox>(); 1089 } 1090 1091 1092 private class FilterTreeItem extends JCheckBox { 1093 public FilterTreeItem(String logName, int level, String levelName) { 1094 super(levelName); 1095 this.logName = logName; 1096 this.level = level; 1097 } 1098 String logName; 1099 int level; 1100 } 1101 1102 private class FilterComboboxItem { 1103 1104 public FilterComboboxItem(String txt, Level l) { 1105 label = txt; 1106 level = l.intValue(); 1107 kind = false; 1108 } 1109 1110 public FilterComboboxItem(String txt, boolean s) { 1111 label = txt; 1112 select = s; 1113 kind = true; 1114 } 1115 1116 1117 public String toString() { 1118 return label; 1119 } 1120 1121 // true - select all / unselect all 1122 // false - select particular level 1123 boolean kind; 1124 1125 private String label; 1126 int level; 1127 boolean select; 1128 1129 } 1130 1131 1132 private class FilterComboBoxListener implements ActionListener { 1133 public FilterComboBoxListener(LoggersTreeModel m) { 1134 model = m; 1135 } 1136 1137 public void actionPerformed(ActionEvent e) { 1138 JComboBox cb = (JComboBox) e.getSource(); 1139 Object o = cb.getSelectedItem(); 1140 if (o instanceof FilterComboboxItem) { 1141 FilterComboboxItem fc = (FilterComboboxItem) o; 1142 DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot(); 1143 if (debug > 1 && fc.kind) { 1144 System.err.println(fc.select ? "Select all" : "Unselect all"); 1145 } 1146 for (int i=0; i < root.getChildCount(); i++) { 1147 DefaultMutableTreeNode fstLevelCh = (DefaultMutableTreeNode) root.getChildAt(i); 1148 PropagatedCheckBox chLog = (PropagatedCheckBox) fstLevelCh.getUserObject(); 1149 if (fc.kind) { 1150 boolean needToFire = chLog.isSelected() == fc.select; 1151 chLog.setSelected(fc.select); 1152 if (needToFire) { 1153 chLog.fireEvent(); 1154 } 1155 } else { 1156 boolean allSelected = true; 1157 for (int j=0; j < fstLevelCh.getChildCount(); j++) { 1158 DefaultMutableTreeNode secondLevelCh = (DefaultMutableTreeNode) fstLevelCh.getChildAt(j); 1159 FilterTreeItem chLev = (FilterTreeItem) secondLevelCh.getUserObject(); 1160 if (chLev.level == fc.level && !chLev.isSelected()) { 1161 chLev.setSelected(true); 1162 filterTree.repaint(); 1163 } else { 1164 allSelected = allSelected && chLev.isSelected(); 1165 } 1166 } 1167 if (allSelected && !chLog.isSelected()) { 1168 chLog.setSelected(true); 1169 filterTree.repaint(); 1170 } 1171 } 1172 } 1173 cb.setSelectedIndex(0); 1174 } 1175 } 1176 1177 private LoggersTreeModel model; 1178 1179 } 1180 1181 public void setVisible(boolean b) { 1182 super.setVisible(b); 1183 if (!b) { 1184 dispose(); 1185 } 1186 } 1187 1188 public void dispose() { 1189 removeWindowFromList(); 1190 if (noWindow) { 1191 return; 1192 } 1193 noWindow = true; 1194 model.dispose(); 1195 super.dispose(); 1196 try { 1197 doc.remove(0, doc.getLength()); 1198 } catch (BadLocationException ex) { 1199 ex.printStackTrace(); 1200 } 1201 thePane = null; 1202 doc = null; 1203 model = null; 1204 workDir = null; 1205 scrollPane = null; 1206 if (debug > 1 && log != null) { 1207 log.info("LogViewer closed"); 1208 } 1209 log = null; 1210 } 1211 1212 private void removeWindowFromList() { 1213 windowList.remove(windowCounter); 1214 } 1215 1216 private void addWindowToList() { 1217 for (int i = 1; i < Integer.MAX_VALUE; i++) { 1218 if (!windowList.contains(i)) { 1219 windowCounter = i; 1220 windowList.add(i); 1221 return; 1222 } 1223 } 1224 windowCounter = 0; 1225 } 1226 1227 private class LV_Scroller extends Thread { 1228 1229 public LV_Scroller() { 1230 super("LV_scroller"); 1231 } 1232 1233 public void run() { 1234 try { 1235 while (true) { 1236 if (noWindow) return; 1237 boolean reset = false; 1238 if (model.isStableState()) { 1239 if (autoScroll) { 1240 reset = thePane.page != model.pagesRead(); 1241 } else { 1242 reset = thePane.page > model.pagesRead() || thePane.page == 0; 1243 } 1244 } 1245 if (reset) { 1246 setPage(model.pagesRead()); 1247 setBusy(!model.isStableState()); 1248 sleep(500); 1249 continue; 1250 } 1251 if (model.isStableState()) { 1252 setBusy(false); 1253 if (autoScroll) { 1254 synchronized (thePane) { 1255 final JScrollBar sb = scrollPane.getVerticalScrollBar(); 1256 if (sb != null) { 1257 SwingUtilities.invokeLater(new Runnable() { 1258 public void run() { 1259 sb.setValue(sb.getMaximum()); 1260 } 1261 1262 }); 1263 } 1264 } 1265 } else { 1266 if (thePane.getCaret() == null) 1267 thePane.setCaretPosition(0); 1268 } 1269 } else { 1270 setBusy(true); 1271 } 1272 sleep(500); 1273 } 1274 } catch (InterruptedException ex) { 1275 // ok 1276 } 1277 } 1278 1279 private void setBusy(boolean b) { 1280 if (!b) { 1281 processLabel.setText(""); 1282 } else { 1283 String oldText = processLabel.getText(); 1284 if ("".equals(oldText) || working1.equals(oldText)) { 1285 processLabel.setText(working2); 1286 } else { 1287 processLabel.setText(working1); 1288 } 1289 } 1290 } 1291 } 1292 1293 private UIFactory uif; 1294 1295 private final Level [] levels = { Level.SEVERE, Level.WARNING, Level.INFO, Level.FINE } ; 1296 private final String [] levelNames = 1297 {LoggerFactory.getLocalizedLevelName(Level.SEVERE), 1298 LoggerFactory.getLocalizedLevelName(Level.WARNING), 1299 LoggerFactory.getLocalizedLevelName(Level.INFO), 1300 LoggerFactory.getLocalizedLevelName(Level.FINE) } ; 1301 1302 private DefaultMutableTreeNode treeRoot; 1303 private JComboBox<?> filterCombo; 1304 private JTree filterTree ; 1305 1306 private boolean noWindow = false; 1307 private JLabel lblPageCounter; 1308 private JButton btnFirst; 1309 private JButton btnLast; 1310 private JButton btnNext; 1311 private JButton btnPrev; 1312 private JCheckBox autoScrollCheckBox; 1313 private JLabel counter; 1314 private JLabel currPage; 1315 private JButton btnSave; 1316 private JButton btnClear; 1317 private JPanel naviPanel; 1318 private JScrollPane scrollPane; 1319 private LogPane thePane; 1320 private JLabel loggerCounter; 1321 private JLabel pageCounter; 1322 private JLabel processLabel; 1323 private WorkDirectory workDir; 1324 private Thread editorThread; 1325 1326 private StyledDocument doc; 1327 private Style styleMsg, styleInfo, styleWarning, styleSevere, styleOther, styleWait, selected; 1328 private FilteredLogModel model; 1329 private Logger log; 1330 1331 private final int debug = 0; 1332 private final int debugPages = 0; 1333 private boolean autoScroll = true; 1334 1335 private FilterComboBoxListener filterComboBoxListener; 1336 1337 private JScrollPane filterTreeScroll; 1338 1339 private boolean wordWrap = false; 1340 1341 private Preferences prefs = Preferences.access(); 1342 private static final String AUTOSCROLL_PREF = "logviewer.autoScroll"; 1343 private static final String WORDWRAP_PREF = "logviewer.wordWrap"; 1344 private int windowCounter = 0; 1345 private static HashSet<Integer> windowList = new HashSet<Integer>(); 1346 private String working1; 1347 private String working2; 1348 1349 }