001package com.ganteater.ae.desktop.view; 002 003import java.awt.BorderLayout; 004import java.awt.Color; 005import java.awt.Desktop; 006import java.awt.FileDialog; 007import java.awt.Font; 008import java.awt.event.ActionEvent; 009import java.awt.event.ActionListener; 010import java.awt.event.ComponentAdapter; 011import java.awt.event.ComponentEvent; 012import java.awt.event.KeyAdapter; 013import java.awt.event.KeyEvent; 014import java.awt.event.MouseAdapter; 015import java.awt.event.MouseEvent; 016import java.io.File; 017import java.io.FileOutputStream; 018import java.io.IOException; 019import java.io.PrintWriter; 020import java.io.StringReader; 021import java.io.StringWriter; 022import java.io.UnsupportedEncodingException; 023import java.net.URI; 024import java.net.URISyntaxException; 025import java.net.URL; 026import java.util.ArrayList; 027import java.util.List; 028import java.util.Map; 029import java.util.Set; 030 031import javax.swing.AbstractButton; 032import javax.swing.BorderFactory; 033import javax.swing.JButton; 034import javax.swing.JComboBox; 035import javax.swing.JComponent; 036import javax.swing.JOptionPane; 037import javax.swing.JPanel; 038import javax.swing.JScrollPane; 039import javax.swing.JTextArea; 040import javax.swing.JTextField; 041import javax.swing.ScrollPaneConstants; 042import javax.swing.SwingUtilities; 043import javax.swing.border.BevelBorder; 044import javax.swing.text.BadLocationException; 045import javax.swing.text.DefaultCaret; 046import javax.swing.text.Utilities; 047import javax.xml.XMLConstants; 048import javax.xml.transform.OutputKeys; 049import javax.xml.transform.Source; 050import javax.xml.transform.Transformer; 051import javax.xml.transform.TransformerFactory; 052import javax.xml.transform.stream.StreamResult; 053import javax.xml.transform.stream.StreamSource; 054 055import org.apache.commons.io.IOUtils; 056import org.apache.commons.lang.ArrayUtils; 057import org.apache.commons.lang.ObjectUtils; 058import org.apache.commons.lang.StringEscapeUtils; 059import org.apache.commons.lang.StringUtils; 060import org.apache.http.NameValuePair; 061import org.apache.http.client.utils.URLEncodedUtils; 062import org.apache.sling.commons.json.JSONObject; 063 064import com.ganteater.ae.desktop.editor.TaskEditor; 065import com.ganteater.ae.desktop.ui.AEFrame; 066import com.ganteater.ae.desktop.ui.TextPrompt; 067import com.ganteater.ae.desktop.view.ListLogPresenter.LogRecord; 068import com.ganteater.ae.processor.Processor; 069import com.ganteater.ae.util.AEUtils; 070 071public class TextLogPresenter extends LogPresenter { 072 073 private static final long serialVersionUID = 1L; 074 075 private static final int MAX_FORMAT_LENGTH = 1024000; 076 077 private JTextArea fLogTextArea; 078 079 private JPanel toolBar = new JPanel(new BorderLayout()); 080 private JTextField statusLineLable = new JTextField(""); 081 private JButton emailMaskButton = new JButton(AEFrame.getIcon("email-mask.png")); 082 private JScrollPane fLogScrollPanel; 083 084 private List<String> supportedTypes = new ArrayList<>(); 085 { 086 supportedTypes.add("txt"); 087 supportedTypes.add("html"); 088 supportedTypes.add("xml"); 089 supportedTypes.add("json"); 090 supportedTypes.add("~json"); 091 supportedTypes.add("url"); 092 supportedTypes.add("path"); 093 supportedTypes.add("uri"); 094 supportedTypes.add("csv"); 095 } 096 097 private JTextField fFindText = new JTextField(12); 098 099 JButton formatButton = new JButton(AEFrame.getIcon("format.png")); 100 101 private Processor processor; 102 103 private FileDialog fileDialog; 104 105 private JComboBox<String> propertiesNames = new JComboBox<String>(); 106 107 private JComboBox<String> typeBox = new JComboBox<>(supportedTypes.toArray(new String[supportedTypes.size()])); 108 109 private Object originData; 110 111 public TextLogPresenter(TaskEditor aTaskEditor, String aName) { 112 super(aName, aTaskEditor.getConfigNode()); 113 114 fLogTextArea = new JTextArea(); 115 DefaultCaret c = new DefaultCaret() { 116 @Override 117 public void setSelectionVisible(boolean visible) { 118 super.setSelectionVisible(true); 119 } 120 }; 121 fLogTextArea.setCaret(c); 122 123 fLogTextArea.setFont(new Font("Courier New", Font.PLAIN, 12)); 124 fLogTextArea.setEditable(true); 125 fLogTextArea.addKeyListener(new KeyAdapter() { 126 @Override 127 public void keyTyped(KeyEvent e) { 128 printStatusLine(); 129 } 130 }); 131 132 fLogTextArea.addMouseListener(new MouseAdapter() { 133 @Override 134 public void mouseReleased(MouseEvent e) { 135 printStatusLine(); 136 } 137 }); 138 139 fLogTextArea.addComponentListener(new ComponentAdapter() { 140 @Override 141 public void componentResized(ComponentEvent e) { 142 if (fLogTextArea.getLineWrap()) { 143 printStatusLine(); 144 } 145 } 146 }); 147 148 fLogScrollPanel = new JScrollPane(); 149 fLogScrollPanel.getViewport().add(fLogTextArea); 150 151 add(fLogScrollPanel, BorderLayout.CENTER); 152 153 formatButton.setToolTipText("Pretty print"); 154 155 JPanel bar = new JPanel(); 156 formatButton.addActionListener(new ActionListener() { 157 public void actionPerformed(ActionEvent e) { 158 format(); 159 } 160 }); 161 162 bar.add(propertiesNames); 163 bar.add(typeBox); 164 bar.add(formatButton); 165 166 JButton button = new JButton(AEFrame.getIcon("placeholder.png")); 167 button.setToolTipText("Placeholder parsing"); 168 169 button.addActionListener(new ActionListener() { 170 public void actionPerformed(ActionEvent e) { 171 String text = fLogTextArea.getText(); 172 try { 173 int caretPosition = fLogTextArea.getCaretPosition(); 174 String replaceProperties = processor.replaceProperties(text); 175 replaceProperties = replaceProperties.replaceAll("<br/>", "\n"); 176 // replaceProperties = 177 // EasyParser.replaseReferense(replaceProperties); 178 setText(replaceProperties); 179 printStatusLine(); 180 fLogTextArea.setCaretPosition(0); 181 if (caretPosition < replaceProperties.length()) 182 fLogTextArea.setCaretPosition(caretPosition); 183 } catch (Exception e1) { 184 e1.printStackTrace(); 185 JOptionPane.showMessageDialog(JOptionPane.getFrameForComponent(TextLogPresenter.this), 186 "$...{} replacer failed."); 187 } 188 } 189 }); 190 191 propertiesNames.addActionListener(new ActionListener() { 192 public void actionPerformed(ActionEvent e) { 193 print(); 194 } 195 }); 196 197 bar.add(button); 198 199 button = new JButton(AEFrame.getIcon("open.png")); 200 button.setToolTipText("Open in defaulf editor"); 201 202 button.addActionListener(new ActionListener() { 203 public void actionPerformed(ActionEvent e) { 204 try { 205 String selectedItem = (String) typeBox.getSelectedItem(); 206 String text = fLogTextArea.getText(); 207 openFile(text, selectedItem); 208 } catch (IOException e1) { 209 JOptionPane.showMessageDialog(JOptionPane.getFrameForComponent(TextLogPresenter.this), 210 "File opening failed."); 211 } 212 } 213 214 }); 215 bar.add(button); 216 217 button = new JButton(AEFrame.getIcon("save.png")); 218 button.setToolTipText("Save to file"); 219 220 button.addActionListener(new ActionListener() { 221 public void actionPerformed(ActionEvent e) { 222 saveToFile(); 223 } 224 }); 225 bar.add(button); 226 227 emailMaskButton.setToolTipText("Masking"); 228 emailMaskButton.setVisible(log.isFilterEnabled()); 229 230 emailMaskButton.addActionListener(new ActionListener() { 231 public void actionPerformed(ActionEvent e) { 232 String text = fLogTextArea.getText(); 233 String maskedText = log.filter(text); 234 setText(maskedText); 235 printStatusLine(); 236 fLogTextArea.setCaretPosition(0); 237 } 238 }); 239 bar.add(emailMaskButton); 240 241 button = new JButton(AEFrame.getIcon("unescape.png")); 242 button.setToolTipText("Unescape"); 243 244 button.addActionListener(new ActionListener() { 245 public void actionPerformed(ActionEvent e) { 246 String text = fLogTextArea.getText(); 247 text = StringEscapeUtils.unescapeXml(text).trim(); 248 text = text.replace("\\n", "\n"); 249 text = text.replace("\\r", "\r"); 250 text = text.replace("\\\"", "\""); 251 setText(text); 252 } 253 254 }); 255 bar.add(button); 256 257 bar.add(fFindText); 258 new TextPrompt("Search", fFindText); 259 260 fFindText.addKeyListener(new KeyAdapter() { 261 @Override 262 public void keyPressed(KeyEvent e) { 263 switch (e.getKeyChar()) { 264 case KeyEvent.VK_ESCAPE: 265 fFindText.setText(""); 266 break; 267 268 case KeyEvent.VK_ENTER: 269 findText(); 270 } 271 } 272 }); 273 274 add(toolBar, BorderLayout.NORTH); 275 276 statusLineLable.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED)); 277 statusLineLable.setEditable(false); 278 add(statusLineLable, BorderLayout.SOUTH); 279 280 fLogTextArea.setEditable(true); 281 282 toolBar.add(bar, BorderLayout.WEST); 283 } 284 285 public void saveToFile() { 286 try { 287 String text = fLogTextArea.getText(); 288 289 text = filter(text); 290 291 if (fileDialog == null) 292 fileDialog = new FileDialog(JOptionPane.getRootFrame(), "Save log record.", FileDialog.SAVE); 293 294 String type = getType(); 295 if (StringUtils.startsWith(type, "~")) { 296 type = StringUtils.substringAfter(type, "~"); 297 } 298 299 String fileName = StringUtils.defaultString(super.getName()); 300 301 if (StringUtils.isNotEmpty(type)) { 302 fileName = fileName + "." + StringUtils.defaultString(type, "txt"); 303 } 304 305 fileDialog.setFile(fileName); 306 fileDialog.setVisible(true); 307 if (fileDialog.getFile() != null) { 308 File theFile = new File(fileDialog.getDirectory(), fileDialog.getFile()); 309 FileOutputStream theFileOutputStream = new FileOutputStream(theFile); 310 theFileOutputStream.write(text.getBytes("UTF-8")); 311 theFileOutputStream.close(); 312 } 313 } catch (Exception e1) { 314 e1.printStackTrace(); 315 JOptionPane.showMessageDialog(JOptionPane.getFrameForComponent(this), "File is not saved."); 316 } 317 } 318 319 protected void printStatusLine() { 320 321 String text = fLogTextArea.getText(); 322 formatButton.setEnabled(text.length() < MAX_FORMAT_LENGTH); 323 int count = countLines(text); 324 325 if (fLogTextArea.getLineWrap()) { 326 if (text.length() < MAX_FORMAT_LENGTH) { 327 int totalCharacters = fLogTextArea.getText().length(); 328 count = (totalCharacters == 0) ? 1 : 0; 329 330 try { 331 int offset = totalCharacters; 332 while (offset > 0) { 333 offset = Utilities.getRowStart(fLogTextArea, offset) - 1; 334 count++; 335 } 336 } catch (BadLocationException e) { 337 e.printStackTrace(); 338 } 339 340 statusLineLable.setText("Lines: " + count + " (Wrapped)"); 341 } else { 342 statusLineLable.setText(""); 343 } 344 } else { 345 int selectedCount = 0; 346 String selectedText = fLogTextArea.getSelectedText(); 347 if (selectedText != null) { 348 selectedCount = countLines(selectedText); 349 } 350 351 statusLineLable 352 .setText("Lines: " + count + (selectedCount > 0 ? "; Selected lines: " + selectedCount : "")); 353 } 354 } 355 356 private int countLines(String text) { 357 char someChar = '\n'; 358 int count = 0; 359 360 if (text != null) { 361 if (text.length() > 0) { 362 count = 1; 363 } 364 365 for (int i = 0; i < text.length(); i++) { 366 if (text.charAt(i) == someChar) { 367 count++; 368 } 369 } 370 } 371 return count; 372 } 373 374 public void info(Object o, Processor processor) { 375 this.processor = processor; 376 setType(o); 377 appendText(o); 378 } 379 380 public void format() { 381 SwingUtilities.invokeLater(() -> { 382 String text = fLogTextArea.getText(); 383 if (text.length() < MAX_FORMAT_LENGTH) { 384 385 String type = StringUtils.upperCase(getType()); 386 387 switch (type) { 388 case "XML": 389 xmlPrettyPrint(text); 390 break; 391 392 case "JSON": 393 try { 394 String jsonBody = StringUtils.trim(text); 395 setText(new JSONObject(jsonBody).toString(4)); 396 printStatusLine(); 397 fLogTextArea.setCaretPosition(0); 398 399 } catch (Exception e1) { 400 setText(text); 401 printStatusLine(); 402 fLogTextArea.setCaretPosition(0); 403 JOptionPane.showMessageDialog(JOptionPane.getFrameForComponent(this), "JSON formating failed."); 404 } 405 break; 406 407 case "~JSON": 408 try { 409 setText(AEUtils.format(text)); 410 printStatusLine(); 411 fLogTextArea.setCaretPosition(0); 412 } catch (Exception e1) { 413 setText(text); 414 printStatusLine(); 415 fLogTextArea.setCaretPosition(0); 416 JOptionPane.showMessageDialog(JOptionPane.getFrameForComponent(this), "JSON formating failed."); 417 } 418 break; 419 420 case "URL": 421 try { 422 StringBuilder formatedText = new StringBuilder(); 423 String trim = text.replace(" ", ""); 424 trim = trim.replace("\n", ""); 425 URL url = new URL(trim); 426 formatedText.append(url.getProtocol() + "://"); 427 formatedText.append(url.getHost()); 428 if (url.getPort() > 0) 429 formatedText.append(":" + url.getPort()); 430 formatedText.append(url.getPath()); 431 432 List<NameValuePair> parse = URLEncodedUtils.parse(url.toURI(), "UTF-8"); 433 if (parse.size() > 0) { 434 formatedText.append("\n"); 435 String delim = "?"; 436 for (NameValuePair nameValuePair : parse) { 437 formatedText.append( 438 delim + nameValuePair.getName() + "=" + nameValuePair.getValue() + "\n"); 439 delim = "&"; 440 } 441 } 442 setText(formatedText.toString()); 443 printStatusLine(); 444 fLogTextArea.setCaretPosition(0); 445 } catch (Exception e1) { 446 JOptionPane.showMessageDialog(JOptionPane.getFrameForComponent(this), 447 "URI transformation failed.\n" + e1.getMessage()); 448 } 449 break; 450 451 case "TXT": 452 if (fLogTextArea.getText().length() < MAX_FORMAT_LENGTH) { 453 boolean wrap = !fLogTextArea.getLineWrap(); 454 fLogScrollPanel 455 .setHorizontalScrollBarPolicy(wrap ? ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER 456 : ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); 457 458 fLogTextArea.setLineWrap(wrap); 459 } else { 460 fLogTextArea.setLineWrap(false); 461 } 462 break; 463 464 } 465 } 466 printStatusLine(); 467 }); 468 } 469 470 private void xmlPrettyPrint(String text) { 471 try { 472 Source xmlInput = new StreamSource(new StringReader(text)); 473 StringWriter stringWriter = new StringWriter(); 474 StreamResult xmlOutput = new StreamResult(stringWriter); 475 TransformerFactory transformerFactory = TransformerFactory.newInstance(); 476 transformerFactory.setAttribute("indent-number", 2); 477 transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); 478 transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, ""); 479 Transformer transformer = transformerFactory.newTransformer(); 480 transformer.setOutputProperty(OutputKeys.INDENT, "yes"); 481 transformer.transform(xmlInput, xmlOutput); 482 483 setText(xmlOutput.getWriter().toString()); 484 printStatusLine(); 485 fLogTextArea.setCaretPosition(0); 486 487 } catch (Exception e1) { 488 setText(text); 489 printStatusLine(); 490 fLogTextArea.setCaretPosition(0); 491 JOptionPane.showMessageDialog(JOptionPane.getFrameForComponent(this), "XML formating failed."); 492 } 493 } 494 495 private void setText(String text) { 496 fLogTextArea.setText(text); 497 } 498 499 protected void setType(Object o) { 500 String type = StringUtils.defaultString(((LogRecord) o).getType(), "txt"); 501 502 if (o instanceof LogRecord) { 503 if (((LogRecord) o).getMessage() instanceof String) { 504 505 if (StringUtils.startsWith((String) ((LogRecord) o).getMessage(), "<?xml")) { 506 type = "xml"; 507 } 508 509 type = StringUtils.defaultString(type, supportedTypes.get(0)); 510 if (!supportedTypes.contains(type.toLowerCase())) { 511 supportedTypes.add(type); 512 this.typeBox.addItem(type); 513 } 514 this.typeBox.setSelectedItem(type); 515 516 } else if (StringUtils.isNotBlank(type)) { 517 if (StringUtils.startsWith(ObjectUtils.toString(o), "<?xml")) { 518 type = "xml"; 519 this.typeBox.setSelectedItem(type); 520 } else { 521 this.typeBox.setSelectedItem(type); 522 if (!StringUtils.equals(this.typeBox.getSelectedItem().toString(), type)) { 523 this.typeBox.addItem(type); 524 } 525 this.typeBox.setSelectedItem(type); 526 } 527 } 528 } 529 } 530 531 private void setPropertiesList(Set keySet) { 532 for (Object object : keySet) { 533 String key = (String) object; 534 if (StringUtils.isNotBlank(key)) { 535 propertiesNames.addItem(key); 536 } 537 } 538 propertiesNames.setVisible(true); 539 } 540 541 private void cleanPropertiesList() { 542 propertiesNames.removeAllItems(); 543 propertiesNames.setVisible(false); 544 } 545 546 @Override 547 public Object debug(Object o) { 548 setType(o); 549 if (o instanceof Throwable) { 550 StringWriter theStringWriter = new StringWriter(); 551 ((Throwable) o).printStackTrace(new PrintWriter(theStringWriter)); 552 appendText(theStringWriter.toString()); 553 } else { 554 appendText(o); 555 } 556 return o; 557 } 558 559 @Override 560 public Object debug(Object o, Throwable aThrowable) { 561 print(o, aThrowable); 562 return o; 563 } 564 565 private void print(Object o, Throwable aThrowable) { 566 setType(o); 567 appendText(o); 568 569 if (aThrowable != null) { 570 StringWriter theStringWriter = new StringWriter(); 571 aThrowable.printStackTrace(new PrintWriter(theStringWriter)); 572 appendText(theStringWriter.toString()); 573 } 574 } 575 576 @Override 577 public Object error(Object o) { 578 print(o, null); 579 return o; 580 } 581 582 @Override 583 public Object error(Object o, Throwable aThrowable) { 584 print(o, aThrowable); 585 return o; 586 } 587 588 public Object warn(Object o) { 589 print(o, null); 590 return o; 591 } 592 593 private void appendText(Object o) { 594 this.originData = o; 595 cleanPropertiesList(); 596 597 LogRecord record = (LogRecord) o; 598 String type = record.getType(); 599 600 Object message = record.getMessage(); 601 if (message instanceof Map && !"json".equals(type)) { 602 Map propertiesData = (Map) ((LogRecord) o).getMessage(); 603 setPropertiesList(propertiesData.keySet()); 604 } 605 606 print(); 607 } 608 609 private void print() { 610 if (originData instanceof LogRecord) { 611 LogRecord rec = (LogRecord) originData; 612 Object data = rec.getMessage(); 613 614 String type = getType(); 615 if (data instanceof Map && "map".equals(type)) { 616 data = ((Map) data).get(propertiesNames.getSelectedItem()); 617 } 618 619 String text; 620 621 if (data instanceof byte[]) { 622 try { 623 text = new String((byte[]) data, "UTF-8"); 624 } catch (UnsupportedEncodingException e) { 625 throw new IllegalArgumentException(e); 626 } 627 } else if (data != null && data.getClass().isArray()) { 628 Object[] array = (Object[]) data; 629 text = ArrayUtils.toString(array); 630 631 } else if (data != null && data instanceof List) { 632 text = StringUtils.join((List) data, "\n"); 633 634 } else if ("json".equals(type)) { 635 if (data instanceof Map) { 636 text = new JSONObject((Map) data).toString(); 637 } else { 638 text = ObjectUtils.toString(data, "<null>"); 639 } 640 } else { 641 text = ObjectUtils.toString(data, "<null>"); 642 } 643 644 setText(text); 645 646 } else { 647 648 setText(ObjectUtils.toString(originData)); 649 } 650 651 fLogTextArea.setCaretPosition(0); 652 printStatusLine(); 653 } 654 655 public String getType() { 656 return (String) TextLogPresenter.this.typeBox.getSelectedItem(); 657 } 658 659 public void warn(Object o, Throwable aThrowable) { 660 print(o, aThrowable); 661 } 662 663 public void findText() { 664 String text = fFindText.getText(); 665 String content = fLogTextArea.getText(); 666 int caretPosition = fLogTextArea.getCaretPosition(); 667 668 int indexOf = StringUtils.indexOfIgnoreCase(content, text, caretPosition); 669 if (indexOf >= 0) { 670 fLogTextArea.setSelectionStart(indexOf); 671 int selectionEnd = indexOf + text.length(); 672 fLogTextArea.setSelectionEnd(selectionEnd); 673 674 } else { 675 caretPosition = 0; 676 indexOf = StringUtils.indexOfIgnoreCase(content, text, caretPosition); 677 678 if (indexOf >= 0) { 679 fLogTextArea.setSelectionStart(indexOf); 680 int selectionEnd = indexOf + text.length(); 681 fLogTextArea.setSelectionEnd(selectionEnd); 682 683 } else { 684 JOptionPane.showMessageDialog(JOptionPane.getFrameForComponent(this), 685 "Text: \"" + text + "\" is not found."); 686 } 687 } 688 689 } 690 691 public void setEnabled(boolean enabled) { 692 if (enabled == false) 693 fLogTextArea.setBackground(Color.lightGray); 694 else 695 fLogTextArea.setBackground(Color.white); 696 } 697 698 public void clear() { 699 setText(""); 700 printStatusLine(); 701 } 702 703 public JComponent getLogTextArea() { 704 return fLogTextArea; 705 } 706 707 public static void openFile(Object originData, String type) throws IOException { 708 709 switch (StringUtils.upperCase(StringUtils.defaultString(type, "txt"))) { 710 case "URL": 711 try { 712 String string = ObjectUtils.toString(originData, ""); 713 URL url = new URL(string); 714 URI uri = url.toURI(); 715 openWebpage(uri); 716 } catch (URISyntaxException e) { 717 new IOException(e); 718 } 719 break; 720 721 case "URI": 722 try { 723 openWebpage(new URI(ObjectUtils.toString(originData, ""))); 724 } catch (URISyntaxException e) { 725 new IOException(e); 726 } 727 break; 728 729 case "PATH": 730 Desktop.getDesktop().open(new File(ObjectUtils.toString(originData, "").trim())); 731 break; 732 733 default: 734 if (StringUtils.startsWith(type, "~")) { 735 type = StringUtils.substringAfter(type, "~"); 736 } 737 File file = File.createTempFile("anteater", "." + StringUtils.defaultString(type, "txt")); 738 byte[] data; 739 if (originData instanceof LogRecord) { 740 Object logdata = ((LogRecord) originData).getMessage(); 741 if (logdata instanceof byte[]) { 742 data = (byte[]) logdata; 743 } else { 744 data = ((LogRecord) originData).toString().getBytes(); 745 } 746 } else { 747 data = ObjectUtils.toString(originData).getBytes(); 748 } 749 750 FileOutputStream output = new FileOutputStream(file); 751 IOUtils.write(data, output); 752 output.flush(); 753 Desktop.getDesktop().open(file); 754 output.close(); 755 } 756 } 757 758 public static void openWebpage(URI uri) { 759 Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null; 760 if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) { 761 try { 762 desktop.browse(uri); 763 } catch (Exception e) { 764 e.printStackTrace(); 765 } 766 } 767 } 768 769 public void addButton(AbstractButton onTop) { 770 toolBar.add(onTop, BorderLayout.EAST); 771 } 772 773 @Override 774 public LogPresenter copyAndClean() { 775 return null; 776 } 777 778 @Override 779 public boolean isFilterEnabled() { 780 return log.isFilterEnabled(); 781 } 782 783 @Override 784 public String filter(Object message) { 785 return log.filter(message); 786 } 787}