001package com.ganteater.ae.desktop.view;
002
003import java.awt.BorderLayout;
004import java.awt.Color;
005import java.awt.Component;
006import java.awt.Font;
007import java.awt.Toolkit;
008import java.awt.datatransfer.Clipboard;
009import java.awt.datatransfer.StringSelection;
010import java.awt.event.ActionEvent;
011import java.awt.event.ActionListener;
012import java.awt.event.KeyAdapter;
013import java.awt.event.KeyEvent;
014import java.awt.event.MouseAdapter;
015import java.awt.event.MouseEvent;
016import java.io.IOException;
017import java.io.PrintWriter;
018import java.io.StringWriter;
019import java.text.SimpleDateFormat;
020import java.util.Date;
021import java.util.HashMap;
022import java.util.Map;
023
024import javax.swing.JButton;
025import javax.swing.JCheckBox;
026import javax.swing.JComboBox;
027import javax.swing.JLabel;
028import javax.swing.JMenuItem;
029import javax.swing.JPanel;
030import javax.swing.JPopupMenu;
031import javax.swing.JScrollBar;
032import javax.swing.JScrollPane;
033import javax.swing.JTable;
034import javax.swing.JTextArea;
035import javax.swing.JTextField;
036import javax.swing.ListSelectionModel;
037import javax.swing.UIDefaults;
038import javax.swing.table.AbstractTableModel;
039import javax.swing.table.TableCellRenderer;
040import javax.swing.table.TableColumnModel;
041
042import org.apache.commons.lang.ObjectUtils;
043import org.apache.commons.lang.StringUtils;
044
045import com.ganteater.ae.AELogRecord;
046import com.ganteater.ae.AEWorkspace;
047import com.ganteater.ae.desktop.editor.TaskEditor;
048import com.ganteater.ae.desktop.ui.AEFrame;
049import com.ganteater.ae.desktop.ui.LogFrame;
050import com.ganteater.ae.desktop.ui.TextPrompt;
051import com.ganteater.ae.desktop.util.UIUtils;
052
053public class ListLogPresenter extends LogPresenter implements ActionListener {
054
055        private static final long serialVersionUID = 1L;
056
057        private static final Font LOG_FONT = new Font("Monospaced", Font.PLAIN, 11);
058
059        private LogList logList;
060
061        private JComboBox<String> levelBox = new JComboBox<String>(new String[] { "Debug", "Info", "Warning", "Error" });
062        private JCheckBox autoformatBtn = new JCheckBox("Auto format");
063        private JTextField find = new JTextField(8);
064
065        private JTable fLogTextArea = new LogTable();
066
067        private JCheckBox fLogToEnd = new JCheckBox("Auto scrolling");
068        private JCheckBox fDeleteOldRec = new JCheckBox("Limit 50 records");
069
070        private JScrollPane fLogScrollPanel = new JScrollPane();
071
072        private TaskEditor fTaskEditor;
073
074        private JLabel messageBar = new JLabel();
075
076        private boolean mainLog;
077
078        public ListLogPresenter(TaskEditor aTaskEditor, String aName) {
079                this(aTaskEditor, aName, false);
080        }
081
082        public ListLogPresenter(TaskEditor aTaskEditor, String aName, boolean mainLog) {
083                super(aName, aTaskEditor.getConfigNode());
084                this.mainLog = mainLog;
085                fTaskEditor = aTaskEditor;
086                createPanel();
087        }
088
089        private void copyToClipboard() {
090                ListSelectionModel selectionModel = fLogTextArea.getSelectionModel();
091                int minSelectionIndex = selectionModel.getMinSelectionIndex();
092                int maxSelectionIndex = selectionModel.getMaxSelectionIndex();
093
094                StringBuilder msgBuilder = new StringBuilder();
095                for (int i = minSelectionIndex; i <= maxSelectionIndex; i++) {
096
097                        boolean selectedIndex = selectionModel.isSelectedIndex(i);
098
099                        if (selectedIndex) {
100                                if (i != minSelectionIndex) {
101                                        msgBuilder.append("\n---------------------\n");
102                                }
103
104                                LogRecord text = logList.get(i);
105                                TextLogPresenter logPanel = new TextLogPresenter(fTaskEditor, getName());
106                                logPanel.info(text, fTaskEditor.getTaskProcessor());
107                                boolean selected = autoformatBtn.isSelected();
108                                if (selected) {
109                                        logPanel.format();
110                                }
111
112                                String msg = ((JTextArea) logPanel.getLogTextArea()).getText();
113                                msgBuilder.append(msg);
114                        }
115                }
116
117                StringSelection stringSelection = new StringSelection(msgBuilder.toString());
118                Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
119                clipboard.setContents(stringSelection, null);
120        }
121
122        public void actionPerformed(ActionEvent e) {
123                if (e != null && e.getSource() == fLogToEnd) {
124                        AEWorkspace.getInstance().setDefaultUserConfiguration(".Edit.LogToEnd",
125                                        fLogToEnd.isSelected() ? "true" : "false");
126                        return;
127                }
128                if (e != null && e.getSource() == fDeleteOldRec) {
129                        AEWorkspace.getInstance().setDefaultUserConfiguration(".Edit.DeleteOldRec",
130                                        fDeleteOldRec.isSelected() ? "true" : "false");
131                        return;
132                }
133        }
134
135        @Override
136        public Object info(Object o) {
137                appendText(2, o);
138                return super.info(o);
139        }
140
141        @Override
142        public Object debug(Object o) {
143                if (o instanceof Throwable) {
144                        StringWriter theStringWriter = new StringWriter();
145                        ((Throwable) o).printStackTrace(new PrintWriter(theStringWriter));
146                        appendText(3, theStringWriter.toString());
147                        return o;
148
149                } else {
150                        appendText(3, o);
151                        return super.debug(o);
152                }
153        }
154
155        @Override
156        public Object debug(Object o, Throwable aThrowable) {
157                appendText(3, o);
158
159                StringWriter theStringWriter = new StringWriter();
160                aThrowable.printStackTrace(new PrintWriter(theStringWriter));
161                appendText(3, theStringWriter.toString());
162
163                return super.error(o, aThrowable);
164        }
165
166        @Override
167        public Object error(Object o) {
168                appendText(0, o);
169                return super.error(o);
170        }
171
172        @Override
173        public Object error(Object o, Throwable aThrowable) {
174                StringWriter theStringWriter = new StringWriter();
175                aThrowable.printStackTrace(new PrintWriter(theStringWriter));
176                appendText(0, ObjectUtils.toString(o) + "\n" + theStringWriter.toString());
177                return super.error(o, aThrowable);
178        }
179
180        @Override
181        public Object warn(Object o) {
182                appendText(1, o);
183                return super.error(o);
184        }
185
186        void appendText(int logLevel, Object o) {
187                logList.add(new LogRecord(logLevel, o), fDeleteOldRec.isSelected());
188
189                int level = 3 - levelBox.getSelectedIndex();
190                if (logLevel <= level) {
191                        fLogTextArea.revalidate();
192                        fLogScrollPanel.repaint();
193                }
194
195                if (fLogToEnd.isSelected()) {
196                        JScrollBar verticalScrollBar = fLogScrollPanel.getVerticalScrollBar();
197                        verticalScrollBar.setValue(verticalScrollBar.getMaximum());
198                }
199        }
200
201        public void warn(Object o, Throwable aThrowable) {
202                appendText(1, o);
203                StringWriter theStringWriter = new StringWriter();
204                aThrowable.printStackTrace(new PrintWriter(theStringWriter));
205                appendText(1, theStringWriter.toString());
206                super.error(o, aThrowable);
207        }
208
209        public void createPanel() {
210                String defaultUserConfiguration = AEWorkspace.getInstance().getDefaultUserConfiguration(".defaultLogLevel",
211                                "2");
212                int defLevel = Integer.parseInt(defaultUserConfiguration);
213                logList = new LogList(defLevel);
214
215                logList.setLevel(defLevel);
216                levelBox.setSelectedIndex(3 - defLevel);
217                levelBox.setFont(LOG_FONT);
218
219                fLogTextArea.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
220                fLogTextArea.setCellSelectionEnabled(true);
221
222                fLogTextArea.addKeyListener(new KeyAdapter() {
223                        @Override
224                        public void keyPressed(KeyEvent e) {
225                                if (e.isControlDown() && e.isShiftDown() && e.getKeyCode() == KeyEvent.VK_C) {
226                                        copyToClipboard();
227                                }
228                        }
229
230                });
231
232                fLogTextArea.addMouseListener(new MouseAdapter() {
233
234                        public void mouseClicked(MouseEvent e) {
235                                if (e.getButton() == 1) {
236
237                                        if (e.getClickCount() == 2) {
238                                                LogRecord logRecord = selectAndGetRecord(e);
239                                                if (!e.isControlDown()) {
240                                                        openLogRecord(logRecord);
241
242                                                } else {
243                                                        openIn(logRecord);
244                                                }
245                                        }
246
247                                } else if (e.getButton() == 3) {
248                                        LogRecord logRecord = selectAndGetRecord(e);
249
250                                        final JPopupMenu popup = new JPopupMenu();
251                                        JMenuItem jMenuItem = new JMenuItem("Open");
252                                        jMenuItem.addActionListener(event -> openLogRecord(logRecord));
253                                        popup.add(jMenuItem);
254                                        jMenuItem = new JMenuItem("System TextEditor");
255                                        jMenuItem.addActionListener(event -> openIn(logRecord));
256                                        popup.add(jMenuItem);
257                                        jMenuItem = new JMenuItem("Copy");
258                                        jMenuItem.addActionListener(event -> copyToClipboard());
259                                        popup.add(jMenuItem);
260                                        jMenuItem = new JMenuItem("Save");
261                                        jMenuItem.addActionListener(event -> {
262                                                ListSelectionModel selectionModel = fLogTextArea.getSelectionModel();
263                                                int minSelectionIndex = selectionModel.getMinSelectionIndex();
264                                                int maxSelectionIndex = selectionModel.getMaxSelectionIndex();
265
266                                                for (int i = minSelectionIndex; i <= maxSelectionIndex; i++) {
267
268                                                        boolean selectedIndex = selectionModel.isSelectedIndex(i);
269
270                                                        if (selectedIndex) {
271                                                                LogRecord rec = logList.get(i);
272                                                                TextLogPresenter logPanel = new TextLogPresenter(fTaskEditor, rec.getSource());
273
274                                                                logPanel.info(rec, fTaskEditor.getTaskProcessor());
275                                                                boolean selected = autoformatBtn.isSelected();
276                                                                if (selected) {
277                                                                        logPanel.format();
278                                                                }
279                                                                logPanel.saveToFile();
280                                                        }
281                                                }
282                                        });
283                                        popup.add(jMenuItem);
284                                        popup.show(e.getComponent(), e.getX(), e.getY());
285                                }
286                        }
287
288                        private LogRecord selectAndGetRecord(MouseEvent e) {
289                                int r = fLogTextArea.rowAtPoint(e.getPoint());
290                                if (r >= 0 && r < fLogTextArea.getRowCount()) {
291                                        fLogTextArea.setRowSelectionInterval(r, r);
292                                } else {
293                                        fLogTextArea.clearSelection();
294                                }
295                                ListSelectionModel selectionModel = fLogTextArea.getSelectionModel();
296                                int anchorSelectionIndex = selectionModel.getAnchorSelectionIndex();
297                                return logList.get(anchorSelectionIndex);
298                        }
299
300                        private void openLogRecord(LogRecord logRecord) {
301                                String source = logRecord.getSource();
302                                source = StringUtils.defaultString(source);
303                                TextLogPresenter logPanel = new TextLogPresenter(fTaskEditor, source);
304                                LogFrame logFrame = new LogFrame(logPanel);
305                                logFrame.setVisible(true);
306                                logFrame.setAlwaysOnTop(true);
307                                logFrame.setAlwaysOnTop(false);
308
309                                logFrame.showText(logRecord, fTaskEditor.getTaskProcessor(), autoformatBtn.isSelected());
310                        }
311
312                        private void openIn(LogRecord text) {
313                                try {
314                                        TextLogPresenter.openFile(text, text.getType());
315                                } catch (IOException e1) {
316                                        e1.printStackTrace();
317                                }
318                        }
319                });
320
321                TableColumnModel columnModel = fLogTextArea.getColumnModel();
322                columnModel.getColumn(0).setMinWidth(0);
323                columnModel.getColumn(0).setPreferredWidth(8);
324                columnModel.getColumn(0).setMaxWidth(100);
325                columnModel.getColumn(0).setHeaderValue("Time");
326                columnModel.getColumn(1).setHeaderValue("Message");
327                columnModel.getColumn(2).setHeaderValue("Size, B");
328                columnModel.getColumn(2).setMinWidth(0);
329                columnModel.getColumn(2).setMaxWidth(150);
330                columnModel.getColumn(2).setPreferredWidth(8);
331
332                fLogScrollPanel.getVerticalScrollBar().setDoubleBuffered(true);
333
334                add(fLogScrollPanel, BorderLayout.CENTER);
335
336                JPanel theFindPanel = new JPanel();
337
338                theFindPanel.add(find);
339                new TextPrompt("Search", find);
340
341                find.addKeyListener(new KeyAdapter() {
342                        @Override
343                        public void keyPressed(KeyEvent e) {
344                                if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
345                                        find.setText("");
346                                }
347                                fLogTextArea.repaint();
348                        }
349                });
350                JLabel comp = new JLabel("Level:");
351                comp.setFont(LOG_FONT);
352                theFindPanel.add(comp);
353                levelBox.addActionListener(e -> {
354                        ListSelectionModel selectionModel = fLogTextArea.getSelectionModel();
355                        selectionModel.clearSelection();
356                        int logLevel = 3 - levelBox.getSelectedIndex();
357                        AEWorkspace.getInstance().setDefaultUserConfiguration(".defaultLogLevel", Integer.toString(logLevel));
358                        logList.setLevel(logLevel);
359                        fLogTextArea.revalidate();
360                        fLogScrollPanel.revalidate();
361                });
362                theFindPanel.add(levelBox);
363
364                JButton theClearButton = new JButton(AEFrame.getIcon("clean.png"));
365                theClearButton.setFont(LOG_FONT);
366                theClearButton.addActionListener(e -> {
367                        ListSelectionModel selectionModel = fLogTextArea.getSelectionModel();
368                        selectionModel.clearSelection();
369                        logList.clear();
370                        fLogTextArea.revalidate();
371                        fLogScrollPanel.revalidate();
372                });
373
374                theFindPanel.add(theClearButton);
375
376                if (!mainLog) {
377                        JButton theCloseButton = new JButton(AEFrame.getIcon("close.png"));
378                        theCloseButton.setFont(LOG_FONT);
379                        theCloseButton.addActionListener(e -> fTaskEditor.removeActivePresentationPanel());
380
381                        theFindPanel.add(theCloseButton);
382                }
383
384                JPanel theLogControlPanel = new JPanel(new BorderLayout());
385                theLogControlPanel.add(theFindPanel, BorderLayout.EAST);
386                JPanel theLogLevelPanel = new JPanel();
387
388                theLogControlPanel.add(theLogLevelPanel, BorderLayout.WEST);
389
390                add(theLogControlPanel, BorderLayout.NORTH);
391
392                JPanel panel = new JPanel(new BorderLayout());
393                JPanel panel4 = new JPanel();
394
395                panel4.add(fLogToEnd);
396                panel4.add(fDeleteOldRec);
397
398                autoformatBtn.setSelected(
399                                "true".equals(AEWorkspace.getInstance().getDefaultUserConfiguration(".Edit.AutoFormat", "false")));
400                autoformatBtn.addChangeListener(e -> AEWorkspace.getInstance().setDefaultUserConfiguration(".Edit.AutoFormat",
401                                autoformatBtn.isSelected() ? "true" : "false"));
402
403                panel4.add(autoformatBtn);
404
405                panel4.add(this.messageBar);
406                panel.add(panel4, BorderLayout.WEST);
407                add(panel, BorderLayout.SOUTH);
408                fLogToEnd.setSelected(
409                                "true".equals(AEWorkspace.getInstance().getDefaultUserConfiguration(".Edit.LogToEnd", "true")));
410                fLogToEnd.addActionListener(this);
411
412                fDeleteOldRec.setSelected(
413                                "true".equals(AEWorkspace.getInstance().getDefaultUserConfiguration(".Edit.DeleteOldRec", "true")));
414                fDeleteOldRec.addActionListener(this);
415
416                fLogTextArea.setFont(LOG_FONT);
417                fLogScrollPanel.getViewport().add(fLogTextArea);
418        }
419
420        public ListLogPresenter copyAndClean() {
421                ListLogPresenter listLogPresenter = new ListLogPresenter(fTaskEditor, getName());
422                listLogPresenter.setLogList(logList);
423                String defaultUserConfiguration = AEWorkspace.getInstance().getDefaultUserConfiguration(".defaultLogLevel",
424                                "2");
425                int defLevel = Integer.parseInt(defaultUserConfiguration);
426                logList = new LogList(defLevel);
427                fLogTextArea.revalidate();
428                fLogScrollPanel.repaint();
429
430                return listLogPresenter;
431        }
432
433        private void setLogList(LogList logList) {
434                this.logList = logList;
435                fLogToEnd.setVisible(false);
436                fDeleteOldRec.setVisible(false);
437        }
438
439        private final class LogTable extends JTable {
440                private static final long serialVersionUID = 1L;
441
442                private LogTable() {
443                        super(new LogTableModel());
444                }
445
446                @Override
447                public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
448                        Component c = super.prepareRenderer(renderer, row, column);
449
450                        UIDefaults defaults = javax.swing.UIManager.getDefaults();
451                        Color background = defaults.getColor("List.background");
452                        Color foreground = defaults.getColor("List.foreground");
453
454                        LogRecord logRecord = logList.get(row);
455                        int level = logRecord.getLogLevel();
456
457                        switch (column) {
458                        case 0:
459                                long time = logRecord.getTime();
460                                double a = time / 500.0;
461                                double b = (Math.sin(a) + 1);
462                                float r = 1 - (float) b / 2;
463                                background = new Color(r, r, r);
464                                foreground = UIUtils.getContrastColor(background);
465                                break;
466
467                        case 2:
468                                String text = logRecord.toString();
469                                if (text != null) {
470                                        a = (double) (text.length()) / 100;
471                                        if (a < 1) {
472                                                a = 1;
473                                        }
474                                        b = Math.log(a) * 8;
475                                        b = (100 - b / 2) / 100;
476                                        background = new Color((float) b, (float) b, (float) b);
477                                } else {
478                                        background = Color.YELLOW;
479                                }
480                                foreground = UIUtils.getContrastColor(background);
481                                break;
482                        }
483
484                        if (column == 1) {
485                                switch (level) {
486                                case 0:
487                                        foreground = tuneColorByBackground(Color.RED);
488                                        break;
489                                case 1:
490                                        foreground = tuneColorByBackground(Color.BLUE);
491                                        break;
492                                case 2:
493                                        foreground = tuneColorByBackground(Color.GREEN);
494                                        break;
495                                case 3:
496                                        break;
497                                default:
498                                }
499
500                                String text = find.getText();
501                                if (StringUtils.isNotBlank(text)) {
502                                        String message = ObjectUtils.toString(logRecord.getMessage());
503                                        if (logRecord.getSource() != null) {
504                                                message = logRecord.getSource() + " = " + message;
505                                        }
506                                        int indexOf = StringUtils.indexOfIgnoreCase(message, text);
507                                        if (indexOf >= 0) {
508                                                background = Color.yellow;
509                                                foreground = UIUtils.getContrastColor(background);
510                                        }
511                                }
512                        }
513
514                        ListSelectionModel selectionModel = fLogTextArea.getSelectionModel();
515
516                        if (selectionModel.isSelectedIndex(row) && column == 1) {
517                                c.setBackground(foreground);
518                                if (background == Color.yellow) {
519                                        c.setForeground(background);
520                                } else {
521                                        c.setForeground(UIUtils.getContrastColor(foreground));
522                                }
523                        } else {
524                                c.setBackground(background);
525                                c.setForeground(foreground);
526                        }
527
528                        return c;
529                }
530
531                private Color tuneColorByBackground(Color fgc) {
532                        UIDefaults defaults = javax.swing.UIManager.getDefaults();
533                        Color background = defaults.getColor("List.background");
534
535                        int alpha = (int) (0.2126 * background.getRed() + 0.7152 * background.getGreen()
536                                        + 0.0722 * background.getBlue());
537
538                        if (alpha < 60) {
539                                int colorLevel = 140;
540                                fgc = new Color(fgc.getRed() == 0 ? colorLevel : fgc.getRed(),
541                                                fgc.getGreen() == 0 ? colorLevel : fgc.getGreen(),
542                                                fgc.getBlue() == 0 ? colorLevel : fgc.getBlue());
543                        }
544
545                        if (alpha > 200) {
546                                fgc = fgc.darker().darker();
547                        }
548                        return fgc;
549                }
550        }
551
552        private final class LogTableModel extends AbstractTableModel {
553                private static final long serialVersionUID = 1L;
554                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH.mm.ss,SSS");
555
556                public int getColumnCount() {
557                        return 3;
558                }
559
560                public int getRowCount() {
561                        return logList.getSize();
562                }
563
564                public Object getValueAt(int rowIndex, int columnIndex) {
565
566                        String text = logList.get(rowIndex).toString();
567
568                        switch (columnIndex) {
569                        case 0:
570                                return simpleDateFormat.format(logList.get(rowIndex).time);
571                        case 1:
572                                if (logList.get(rowIndex).getSource() != null) {
573                                        if (text != null) {
574                                                return logList.get(rowIndex).getSource() + " = " + text;
575                                        } else {
576                                                return logList.get(rowIndex).getSource() + " is not defined.";
577                                        }
578                                } else {
579                                        return text;
580                                }
581                        case 2:
582                                return text == null ? 0 : text.length();
583                        }
584                        return null;
585                }
586        }
587
588        public static class LogRecord {
589
590                private Object message;
591
592                private int logLevel;
593
594                private Date time;
595
596                private String type;
597
598                private String source;
599
600                public LogRecord(int logLevel, Object o) {
601                        if (o instanceof Map) {
602                                Map map = (Map) o;
603                                message = new HashMap();
604                                ((Map) message).putAll(map);
605
606                        } else if (o instanceof AELogRecord) {
607                                type = ((AELogRecord) o).getType();
608                                message = ((AELogRecord) o).getMessage();
609
610                                if (message instanceof Map) {
611                                        Map map = (Map) message;
612                                        message = new HashMap();
613                                        ((Map) message).putAll(map);
614                                }
615
616                                source = ((AELogRecord) o).getVarName();
617                        } else {
618                                message = o;
619                        }
620                        this.logLevel = logLevel;
621                        this.time = new Date();
622                }
623
624                public Object getMessage() {
625                        return message;
626                }
627
628                public long getTime() {
629                        return time.getTime();
630                }
631
632                public int getLogLevel() {
633                        return logLevel;
634                }
635
636                public String getType() {
637                        return type;
638                }
639
640                @Override
641                public String toString() {
642                        if (message instanceof byte[]) {
643                                return new String((byte[]) message);
644                        }
645                        return ObjectUtils.toString(message, null);
646                }
647
648                public String getSource() {
649                        return source;
650                }
651
652                public void setSource(String source) {
653                        this.source = source;
654                }
655
656                public String getText() {
657                        return ObjectUtils.toString(message);
658                }
659
660        }
661
662        @Override
663        public boolean isFilterEnabled() {
664                return log.isFilterEnabled();
665        }
666
667        @Override
668        public String filter(Object message) {
669                return log.filter(message);
670        }
671
672}