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