ability to write simple text messages to log console
[fedora-idea.git] / platform / lang-impl / src / com / intellij / diagnostic / logging / LogConsoleBase.java
blob88cca88656749c74c06cec10c217680fbdfa1392
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.intellij.diagnostic.logging;
19 import com.intellij.execution.filters.TextConsoleBuilder;
20 import com.intellij.execution.filters.TextConsoleBuilderFactory;
21 import com.intellij.execution.process.ProcessAdapter;
22 import com.intellij.execution.process.ProcessEvent;
23 import com.intellij.execution.process.ProcessHandler;
24 import com.intellij.execution.ui.ConsoleView;
25 import com.intellij.execution.ui.ConsoleViewContentType;
26 import com.intellij.openapi.actionSystem.*;
27 import com.intellij.openapi.application.ApplicationManager;
28 import com.intellij.openapi.diagnostic.Logger;
29 import com.intellij.openapi.editor.Document;
30 import com.intellij.openapi.editor.Editor;
31 import com.intellij.openapi.project.Project;
32 import com.intellij.openapi.util.Comparing;
33 import com.intellij.openapi.util.Condition;
34 import com.intellij.openapi.util.Disposer;
35 import com.intellij.openapi.util.Key;
36 import com.intellij.openapi.util.io.FileUtil;
37 import com.intellij.ui.FilterComponent;
38 import org.jetbrains.annotations.NotNull;
39 import org.jetbrains.annotations.Nullable;
41 import javax.swing.*;
42 import javax.swing.event.ChangeEvent;
43 import java.awt.*;
44 import java.awt.event.ActionEvent;
45 import java.awt.event.ActionListener;
46 import java.awt.event.KeyEvent;
47 import java.io.*;
48 import java.util.List;
50 /**
51 * Created by IntelliJ IDEA.
52 * User: Eugene.Kudelevsky
53 * Date: Sep 11, 2009
54 * Time: 9:26:11 PM
55 * To change this template use File | Settings | File Templates.
57 public abstract class LogConsoleBase extends AdditionalTabComponent implements LogConsole, LogFilterListener {
58 private static final Logger LOG = Logger.getInstance("com.intellij.diagnostic.logging.LogConsoleImpl");
60 private ConsoleView myConsole;
61 private final LightProcessHandler myProcessHandler = new LightProcessHandler();
62 private ReaderThread myReaderThread;
63 private StringBuffer myOriginalDocument = null;
64 private String myLineUnderSelection = null;
65 private int myLineOffset = -1;
66 private LogContentPreprocessor myContentPreprocessor;
67 private String myTitle = null;
68 private boolean myWasInitialized;
69 private final JPanel myTopComponent = new JPanel(new BorderLayout());
70 private ActionGroup myActions;
71 private final boolean myBuildInActions;
72 private LogFilterModel myModel;
74 private FilterComponent myFilter = new FilterComponent("LOG_FILTER_HISTORY", 5) {
75 public void filter() {
76 myModel.updateCustomFilter(getFilter());
79 private JPanel mySearchComponent;
80 private JComboBox myLogFilterCombo;
81 private JPanel myTextFilterWrapper;
83 public LogConsoleBase(Project project, @Nullable Reader reader, String title, final boolean buildInActions, LogFilterModel model) {
84 super(new BorderLayout());
85 myTitle = title;
86 myModel = model;
87 myReaderThread = new ReaderThread(reader);
88 myBuildInActions = buildInActions;
89 TextConsoleBuilder builder = TextConsoleBuilderFactory.getInstance().createBuilder(project);
90 myConsole = builder.getConsole();
91 myConsole.attachToProcess(myProcessHandler);
92 myModel.addFilterListener(this);
95 @SuppressWarnings({"IOResourceOpenedButNotSafelyClosed"})
96 public LogConsoleBase(Project project, File file, long skippedContents, String title, boolean buildInActions, LogFilterModel model) {
97 this(project, getReader(file, skippedContents), title, buildInActions, model);
100 public LogConsoleBase(Project project, Reader reader, long skippedContents, String title, boolean buildInActions, LogFilterModel model) {
101 this(project, getReader(reader, skippedContents), title, buildInActions, model);
104 @Nullable
105 private static Reader getReader(Reader reader, long skippedContents) {
106 reader = new BufferedReader(reader);
107 try {
108 reader.skip(skippedContents);
110 catch (IOException e) {
111 reader = null;
113 return reader;
116 @Nullable
117 private static Reader getReader(File file, long skippedContents) {
118 Reader reader = null;
119 try {
120 try {
121 final FileInputStream inputStream = new FileInputStream(file);
122 reader = new BufferedReader(new InputStreamReader(inputStream));
123 if (file.length() >= skippedContents) { //do not skip forward
124 inputStream.skip(skippedContents);
127 catch (FileNotFoundException e) {
128 if (FileUtil.createIfDoesntExist(file)) {
129 reader = new BufferedReader(new FileReader(file));
133 catch (Throwable e) {
134 reader = null;
136 return reader;
139 public void setFilterModel(LogFilterModel model) {
140 if (myModel != null) {
141 myModel.removeFilterListener(this);
143 myModel = model;
144 myModel.addFilterListener(this);
147 public LogFilterModel getFilterModel() {
148 return myModel;
151 public LogContentPreprocessor getContentPreprocessor() {
152 return myContentPreprocessor;
155 public void setContentPreprocessor(final LogContentPreprocessor contentPreprocessor) {
156 myContentPreprocessor = contentPreprocessor;
159 @SuppressWarnings({"NonStaticInitializer"})
160 private JComponent createToolbar() {
161 String customFilter = myModel.getCustomFilter();
163 myFilter.reset();
164 myFilter.setSelectedItem(customFilter != null ? customFilter : "");
165 new AnAction() {
167 registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, KeyEvent.SHIFT_DOWN_MASK)),
168 LogConsoleBase.this);
171 public void actionPerformed(final AnActionEvent e) {
172 myFilter.requestFocusInWindow();
176 if (myBuildInActions) {
177 final JComponent tbComp =
178 ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, getOrCreateActions(), true).getComponent();
179 myTopComponent.add(tbComp, BorderLayout.CENTER);
180 myTopComponent.add(getSearchComponent(), BorderLayout.EAST);
184 return myTopComponent;
187 public ActionGroup getOrCreateActions() {
188 if (myActions != null) return myActions;
189 DefaultActionGroup group = new DefaultActionGroup();
191 final AnAction[] actions = myConsole.createConsoleActions();
192 for (AnAction action : actions) {
193 group.add(action);
196 group.addSeparator();
198 /*for (final LogFilter filter : filters) {
199 group.add(new ToggleAction(filter.getName(), filter.getName(), filter.getIcon()) {
200 public boolean isSelected(AnActionEvent e) {
201 return prefs.isFilterSelected(filter);
204 public void setSelected(AnActionEvent e, boolean state) {
205 prefs.setFilterSelected(filter, state);
210 myActions = group;
212 return myActions;
215 public void onFilterStateChange(final LogFilter filter) {
216 filterConsoleOutput(new Condition<String>() {
217 public boolean value(final String line) {
218 return myModel.isApplicable(line);
223 public void onTextFilterChange() {
224 filterConsoleOutput(new Condition<String>() {
225 public boolean value(final String line) {
226 return myModel.isApplicable(line);
231 @NotNull
232 public JComponent getComponent() {
233 if (!myWasInitialized) {
234 myWasInitialized = true;
235 add(myConsole.getComponent(), BorderLayout.CENTER);
236 add(createToolbar(), BorderLayout.NORTH);
238 return this;
241 public abstract boolean isActive();
243 public void activate() {
244 if (myReaderThread == null) return;
245 if (isActive() && !myReaderThread.myRunning) {
246 myFilter.setSelectedItem(myModel.getCustomFilter());
247 myReaderThread.startRunning();
248 ApplicationManager.getApplication().executeOnPooledThread(myReaderThread);
250 else if (!isActive() && myReaderThread.myRunning) {
251 myReaderThread.stopRunning();
255 public void stateChanged(final ChangeEvent e) {
256 activate();
259 public String getTabTitle() {
260 return myTitle;
263 public void dispose() {
264 myModel.removeFilterListener(this);
265 if (myReaderThread != null && myReaderThread.myReader != null) {
266 myReaderThread.stopRunning();
267 try {
268 myReaderThread.myReader.close();
270 catch (IOException e) {
271 LOG.warn(e);
273 myReaderThread.myReader = null;
274 myReaderThread = null;
276 if (myConsole != null) {
277 Disposer.dispose(myConsole);
278 myConsole = null;
280 if (myFilter != null) {
281 myFilter.dispose();
282 myFilter = null;
284 myOriginalDocument = null;
287 private void stopRunning() {
288 if (myReaderThread != null && !isActive()) {
289 myReaderThread.stopRunning();
293 protected synchronized void addMessage(final String text) {
294 if (text == null) return;
295 if (myContentPreprocessor != null) {
296 final java.util.List<LogFragment> fragments = myContentPreprocessor.parseLogLine(text + "\n");
297 myOriginalDocument = getOriginalDocument();
298 for (LogFragment fragment : fragments) {
299 myProcessHandler.notifyTextAvailable(fragment.getText(), fragment.getOutputType());
300 if (myOriginalDocument != null) {
301 myOriginalDocument.append(fragment.getText());
305 else {
306 if (myModel.isApplicable(text)) {
307 Key key = myModel.processLine(text);
308 if (key != null) {
309 myProcessHandler.notifyTextAvailable(text + "\n", key);
312 myOriginalDocument = getOriginalDocument();
313 if (myOriginalDocument != null) {
314 myOriginalDocument.append(text).append("\n");
319 public void attachStopLogConsoleTrackingListener(final ProcessHandler process) {
320 if (process != null) {
321 final ProcessAdapter stopListener = new ProcessAdapter() {
322 public void processTerminated(final ProcessEvent event) {
323 process.removeProcessListener(this);
324 stopRunning();
327 process.addProcessListener(stopListener);
331 private StringBuffer getOriginalDocument() {
332 if (myOriginalDocument == null) {
333 final Editor editor = getEditor();
334 if (editor != null) {
335 myOriginalDocument = new StringBuffer(editor.getDocument().getText());
338 return myOriginalDocument;
341 @Nullable
342 private Editor getEditor() {
343 return myConsole != null ? (Editor)((DataProvider)myConsole).getData(DataConstants.EDITOR) : null;
346 private synchronized void filterConsoleOutput(Condition<String> isApplicable) {
347 myOriginalDocument = getOriginalDocument();
348 if (myOriginalDocument != null) {
349 final Editor editor = getEditor();
350 LOG.assertTrue(editor != null);
351 final Document document = editor.getDocument();
352 final int caretOffset = editor.getCaretModel().getOffset();
353 if (caretOffset > -1) {
354 int line = document.getLineNumber(caretOffset);
355 if (line > -1 && line < document.getLineCount()) {
356 final int startOffset = document.getLineStartOffset(line);
357 myLineUnderSelection = document.getText().substring(startOffset, document.getLineEndOffset(line));
358 myLineOffset = caretOffset - startOffset;
361 myConsole.clear();
362 final String[] lines = myOriginalDocument.toString().split("\n");
363 int offset = 0;
364 boolean caretPositioned = false;
365 for (String line : lines) {
366 if (printMessageToConsole(line, isApplicable)) {
367 if (!caretPositioned) {
368 if (Comparing.strEqual(myLineUnderSelection, line)) {
369 caretPositioned = true;
370 offset += myLineOffset != -1 ? myLineOffset : 0;
372 else {
373 offset += line.length() + 1;
378 myConsole.scrollTo(offset);
382 private boolean printMessageToConsole(String line, Condition<String> isApplicable) {
383 if (myContentPreprocessor != null) {
384 List<LogFragment> fragments = myContentPreprocessor.parseLogLine(line + '\n');
385 for (LogFragment fragment : fragments) {
386 ConsoleViewContentType consoleViewType = ConsoleViewContentType.getConsoleViewType(fragment.getOutputType());
387 if (consoleViewType != null) {
388 myConsole.print(fragment.getText(), consoleViewType);
392 else if (isApplicable.value(line)) {
393 Key key = myModel.processLine(line);
394 if (key != null) {
395 ConsoleViewContentType type = ConsoleViewContentType.getConsoleViewType(key);
396 if (type != null) {
397 myConsole.print(line + "\n", type);
401 return true;
404 public ActionGroup getToolbarActions() {
405 return getOrCreateActions();
408 public String getToolbarPlace() {
409 return ActionPlaces.UNKNOWN;
412 public JComponent getToolbarContextComponent() {
413 return myConsole.getComponent();
416 public JComponent getPreferredFocusableComponent() {
417 return myConsole.getPreferredFocusableComponent();
420 public String getTitle() {
421 return myTitle;
424 public synchronized void clear() {
425 myConsole.clear();
426 myOriginalDocument = null;
429 public JComponent getSearchComponent() {
430 List<? extends LogFilter> filters = myModel.getLogFilters();
431 myLogFilterCombo.setModel(new DefaultComboBoxModel(filters.toArray(new LogFilter[filters.size()])));
432 for (LogFilter filter : filters) {
433 if (myModel.isFilterSelected(filter)) {
434 myLogFilterCombo.setSelectedItem(filter);
435 break;
438 myLogFilterCombo.addActionListener(new ActionListener() {
439 public void actionPerformed(ActionEvent e) {
440 final LogFilter filter = (LogFilter)myLogFilterCombo.getSelectedItem();
441 myModel.selectFilter(filter);
444 myTextFilterWrapper.removeAll();
445 myTextFilterWrapper.add(myFilter);
446 return mySearchComponent;
449 public boolean isContentBuiltIn() {
450 return myBuildInActions;
453 public void writeToConsole(String text, Key outputType) {
454 myProcessHandler.notifyTextAvailable(text, outputType);
457 private static class LightProcessHandler extends ProcessHandler {
458 protected void destroyProcessImpl() {
459 throw new UnsupportedOperationException();
462 protected void detachProcessImpl() {
463 throw new UnsupportedOperationException();
466 public boolean detachIsDefault() {
467 return false;
470 @Nullable
471 public OutputStream getProcessInput() {
472 return null;
476 protected class ReaderThread implements Runnable {
477 private BufferedReader myReader;
478 private boolean myRunning = false;
480 public ReaderThread(@Nullable Reader reader) {
481 myReader = reader != null ? new BufferedReader(reader) : null;
484 public void run() {
485 if (myReader == null) return;
486 while (myRunning) {
487 try {
488 int i = 0;
489 while (i++ < 100) {
490 if (myRunning && myReader != null && myReader.ready()) {
491 addMessage(myReader.readLine());
493 else {
494 break;
497 synchronized (this) {
498 wait(100);
501 catch (IOException e) {
502 LOG.error(e);
504 catch (InterruptedException e) {
505 Disposer.dispose(LogConsoleBase.this);
510 public void startRunning() {
511 myRunning = true;
514 public void stopRunning() {
515 myRunning = false;
516 synchronized (this) {
517 notifyAll();