1 package com
.intellij
.diagnostic
.logging
;
3 import com
.intellij
.execution
.filters
.TextConsoleBuilder
;
4 import com
.intellij
.execution
.filters
.TextConsoleBuilderFactory
;
5 import com
.intellij
.execution
.process
.ProcessAdapter
;
6 import com
.intellij
.execution
.process
.ProcessEvent
;
7 import com
.intellij
.execution
.process
.ProcessHandler
;
8 import com
.intellij
.execution
.ui
.ConsoleView
;
9 import com
.intellij
.execution
.ui
.ConsoleViewContentType
;
10 import com
.intellij
.openapi
.actionSystem
.*;
11 import com
.intellij
.openapi
.application
.ApplicationManager
;
12 import com
.intellij
.openapi
.diagnostic
.Logger
;
13 import com
.intellij
.openapi
.editor
.Document
;
14 import com
.intellij
.openapi
.editor
.Editor
;
15 import com
.intellij
.openapi
.project
.Project
;
16 import com
.intellij
.openapi
.util
.Comparing
;
17 import com
.intellij
.openapi
.util
.Condition
;
18 import com
.intellij
.openapi
.util
.Disposer
;
19 import com
.intellij
.openapi
.util
.Key
;
20 import com
.intellij
.openapi
.util
.io
.FileUtil
;
21 import com
.intellij
.ui
.FilterComponent
;
22 import org
.jetbrains
.annotations
.NotNull
;
23 import org
.jetbrains
.annotations
.Nullable
;
26 import javax
.swing
.event
.ChangeEvent
;
28 import java
.awt
.event
.ActionEvent
;
29 import java
.awt
.event
.ActionListener
;
30 import java
.awt
.event
.KeyEvent
;
32 import java
.util
.List
;
35 * Created by IntelliJ IDEA.
36 * User: Eugene.Kudelevsky
39 * To change this template use File | Settings | File Templates.
41 public abstract class LogConsoleBase
extends AdditionalTabComponent
implements LogConsole
, LogFilterListener
{
42 private static final Logger LOG
= Logger
.getInstance("com.intellij.diagnostic.logging.LogConsoleImpl");
44 private ConsoleView myConsole
;
45 private final LightProcessHandler myProcessHandler
= new LightProcessHandler();
46 private ReaderThread myReaderThread
;
47 private StringBuffer myOriginalDocument
= null;
48 private String myLineUnderSelection
= null;
49 private int myLineOffset
= -1;
50 private LogContentPreprocessor myContentPreprocessor
;
51 private String myTitle
= null;
52 private boolean myWasInitialized
;
53 private final JPanel myTopComponent
= new JPanel(new BorderLayout());
54 private ActionGroup myActions
;
55 private final boolean myBuildInActions
;
56 private LogFilterModel myModel
;
58 private FilterComponent myFilter
= new FilterComponent("LOG_FILTER_HISTORY", 5) {
59 public void filter() {
60 myModel
.updateCustomFilter(getFilter());
63 private JPanel mySearchComponent
;
64 private JComboBox myLogFilterCombo
;
65 private JPanel myTextFilterWrapper
;
67 public LogConsoleBase(Project project
, @Nullable Reader reader
, String title
, final boolean buildInActions
, LogFilterModel model
) {
68 super(new BorderLayout());
71 myReaderThread
= new ReaderThread(reader
);
72 myBuildInActions
= buildInActions
;
73 TextConsoleBuilder builder
= TextConsoleBuilderFactory
.getInstance().createBuilder(project
);
74 myConsole
= builder
.getConsole();
75 myConsole
.attachToProcess(myProcessHandler
);
76 myModel
.addFilterListener(this);
79 @SuppressWarnings({"IOResourceOpenedButNotSafelyClosed"})
80 public LogConsoleBase(Project project
, File file
, long skippedContents
, String title
, boolean buildInActions
, LogFilterModel model
) {
81 this(project
, getReader(file
, skippedContents
), title
, buildInActions
, model
);
84 public LogConsoleBase(Project project
, Reader reader
, long skippedContents
, String title
, boolean buildInActions
, LogFilterModel model
) {
85 this(project
, getReader(reader
, skippedContents
), title
, buildInActions
, model
);
89 private static Reader
getReader(Reader reader
, long skippedContents
) {
90 reader
= new BufferedReader(reader
);
92 reader
.skip(skippedContents
);
94 catch (IOException e
) {
101 private static Reader
getReader(File file
, long skippedContents
) {
102 Reader reader
= null;
105 final FileInputStream inputStream
= new FileInputStream(file
);
106 reader
= new BufferedReader(new InputStreamReader(inputStream
));
107 if (file
.length() >= skippedContents
) { //do not skip forward
108 inputStream
.skip(skippedContents
);
111 catch (FileNotFoundException e
) {
112 if (FileUtil
.createIfDoesntExist(file
)) {
113 reader
= new BufferedReader(new FileReader(file
));
117 catch (Throwable e
) {
123 public void setFilterModel(LogFilterModel model
) {
124 if (myModel
!= null) {
125 myModel
.removeFilterListener(this);
128 myModel
.addFilterListener(this);
131 public LogFilterModel
getFilterModel() {
135 public LogContentPreprocessor
getContentPreprocessor() {
136 return myContentPreprocessor
;
139 public void setContentPreprocessor(final LogContentPreprocessor contentPreprocessor
) {
140 myContentPreprocessor
= contentPreprocessor
;
143 @SuppressWarnings({"NonStaticInitializer"})
144 private JComponent
createToolbar() {
145 String customFilter
= myModel
.getCustomFilter();
148 myFilter
.setSelectedItem(customFilter
!= null ? customFilter
: "");
151 registerCustomShortcutSet(new CustomShortcutSet(KeyStroke
.getKeyStroke(KeyEvent
.VK_TAB
, KeyEvent
.SHIFT_DOWN_MASK
)),
152 LogConsoleBase
.this);
155 public void actionPerformed(final AnActionEvent e
) {
156 myFilter
.requestFocusInWindow();
160 if (myBuildInActions
) {
161 final JComponent tbComp
=
162 ActionManager
.getInstance().createActionToolbar(ActionPlaces
.UNKNOWN
, getOrCreateActions(), true).getComponent();
163 myTopComponent
.add(tbComp
, BorderLayout
.CENTER
);
164 myTopComponent
.add(getSearchComponent(), BorderLayout
.EAST
);
168 return myTopComponent
;
171 public ActionGroup
getOrCreateActions() {
172 if (myActions
!= null) return myActions
;
173 DefaultActionGroup group
= new DefaultActionGroup();
175 final AnAction
[] actions
= myConsole
.createConsoleActions();
176 for (AnAction action
: actions
) {
180 group
.addSeparator();
182 /*for (final LogFilter filter : filters) {
183 group.add(new ToggleAction(filter.getName(), filter.getName(), filter.getIcon()) {
184 public boolean isSelected(AnActionEvent e) {
185 return prefs.isFilterSelected(filter);
188 public void setSelected(AnActionEvent e, boolean state) {
189 prefs.setFilterSelected(filter, state);
199 public void onFilterStateChange(final LogFilter filter
) {
200 filterConsoleOutput(new Condition
<String
>() {
201 public boolean value(final String line
) {
202 return myModel
.isApplicable(line
);
207 public void onTextFilterChange() {
208 filterConsoleOutput(new Condition
<String
>() {
209 public boolean value(final String line
) {
210 return myModel
.isApplicable(line
);
216 public JComponent
getComponent() {
217 if (!myWasInitialized
) {
218 myWasInitialized
= true;
219 add(myConsole
.getComponent(), BorderLayout
.CENTER
);
220 add(createToolbar(), BorderLayout
.NORTH
);
225 public abstract boolean isActive();
227 public void activate() {
228 if (myReaderThread
== null) return;
229 if (isActive() && !myReaderThread
.myRunning
) {
230 myFilter
.setSelectedItem(myModel
.getCustomFilter());
231 myReaderThread
.startRunning();
232 ApplicationManager
.getApplication().executeOnPooledThread(myReaderThread
);
234 else if (!isActive() && myReaderThread
.myRunning
) {
235 myReaderThread
.stopRunning();
239 public void stateChanged(final ChangeEvent e
) {
243 public String
getTabTitle() {
247 public void dispose() {
248 myModel
.removeFilterListener(this);
249 if (myReaderThread
!= null && myReaderThread
.myReader
!= null) {
250 myReaderThread
.stopRunning();
252 myReaderThread
.myReader
.close();
254 catch (IOException e
) {
257 myReaderThread
.myReader
= null;
258 myReaderThread
= null;
260 if (myConsole
!= null) {
261 Disposer
.dispose(myConsole
);
264 if (myFilter
!= null) {
268 myOriginalDocument
= null;
271 private void stopRunning() {
272 if (myReaderThread
!= null && !isActive()) {
273 myReaderThread
.stopRunning();
277 protected synchronized void addMessage(final String text
) {
278 if (text
== null) return;
279 if (myContentPreprocessor
!= null) {
280 final java
.util
.List
<LogFragment
> fragments
= myContentPreprocessor
.parseLogLine(text
+ "\n");
281 myOriginalDocument
= getOriginalDocument();
282 for (LogFragment fragment
: fragments
) {
283 myProcessHandler
.notifyTextAvailable(fragment
.getText(), fragment
.getOutputType());
284 if (myOriginalDocument
!= null) {
285 myOriginalDocument
.append(fragment
.getText());
290 if (myModel
.isApplicable(text
)) {
291 myProcessHandler
.notifyTextAvailable(text
+ "\n", myModel
.processLine(text
));
293 myOriginalDocument
= getOriginalDocument();
294 if (myOriginalDocument
!= null) {
295 myOriginalDocument
.append(text
).append("\n");
300 public void attachStopLogConsoleTrackingListener(final ProcessHandler process
) {
301 if (process
!= null) {
302 final ProcessAdapter stopListener
= new ProcessAdapter() {
303 public void processTerminated(final ProcessEvent event
) {
304 process
.removeProcessListener(this);
308 process
.addProcessListener(stopListener
);
312 private StringBuffer
getOriginalDocument() {
313 if (myOriginalDocument
== null) {
314 final Editor editor
= getEditor();
315 if (editor
!= null) {
316 myOriginalDocument
= new StringBuffer(editor
.getDocument().getText());
319 return myOriginalDocument
;
323 private Editor
getEditor() {
324 return myConsole
!= null ?
(Editor
)((DataProvider
)myConsole
).getData(DataConstants
.EDITOR
) : null;
327 private synchronized void filterConsoleOutput(Condition
<String
> isApplicable
) {
328 myOriginalDocument
= getOriginalDocument();
329 if (myOriginalDocument
!= null) {
330 final Editor editor
= getEditor();
331 LOG
.assertTrue(editor
!= null);
332 final Document document
= editor
.getDocument();
333 final int caretOffset
= editor
.getCaretModel().getOffset();
334 if (caretOffset
> -1) {
335 int line
= document
.getLineNumber(caretOffset
);
336 if (line
> -1 && line
< document
.getLineCount()) {
337 final int startOffset
= document
.getLineStartOffset(line
);
338 myLineUnderSelection
= document
.getText().substring(startOffset
, document
.getLineEndOffset(line
));
339 myLineOffset
= caretOffset
- startOffset
;
343 final String
[] lines
= myOriginalDocument
.toString().split("\n");
345 boolean caretPositioned
= false;
346 for (String line
: lines
) {
347 if (printMessageToConsole(line
, isApplicable
)) {
348 if (!caretPositioned
) {
349 if (Comparing
.strEqual(myLineUnderSelection
, line
)) {
350 caretPositioned
= true;
351 offset
+= myLineOffset
!= -1 ? myLineOffset
: 0;
354 offset
+= line
.length() + 1;
359 myConsole
.scrollTo(offset
);
363 private boolean printMessageToConsole(String line
, Condition
<String
> isApplicable
) {
364 if (myContentPreprocessor
!= null) {
365 List
<LogFragment
> fragments
= myContentPreprocessor
.parseLogLine(line
+ '\n');
366 for (LogFragment fragment
: fragments
) {
367 ConsoleViewContentType consoleViewType
= ConsoleViewContentType
.getConsoleViewType(fragment
.getOutputType());
368 if (consoleViewType
!= null) {
369 myConsole
.print(fragment
.getText(), consoleViewType
);
373 else if (isApplicable
.value(line
)) {
374 Key key
= myModel
.processLine(line
);
375 myConsole
.print(line
+ "\n", ConsoleViewContentType
.getConsoleViewType(key
));
380 public ActionGroup
getToolbarActions() {
381 return getOrCreateActions();
384 public String
getToolbarPlace() {
385 return ActionPlaces
.UNKNOWN
;
388 public JComponent
getToolbarContextComponent() {
389 return myConsole
.getComponent();
392 public JComponent
getPreferredFocusableComponent() {
393 return myConsole
.getPreferredFocusableComponent();
396 public String
getTitle() {
400 public synchronized void clear() {
402 myOriginalDocument
= null;
405 public JComponent
getSearchComponent() {
406 List
<?
extends LogFilter
> filters
= myModel
.getLogFilters();
407 myLogFilterCombo
.setModel(new DefaultComboBoxModel(filters
.toArray(new LogFilter
[filters
.size()])));
408 for (LogFilter filter
: filters
) {
409 if (myModel
.isFilterSelected(filter
)) {
410 myLogFilterCombo
.setSelectedItem(filter
);
414 myLogFilterCombo
.addActionListener(new ActionListener() {
415 public void actionPerformed(ActionEvent e
) {
416 final LogFilter filter
= (LogFilter
)myLogFilterCombo
.getSelectedItem();
417 myModel
.selectFilter(filter
);
420 myTextFilterWrapper
.removeAll();
421 myTextFilterWrapper
.add(myFilter
);
422 return mySearchComponent
;
425 public boolean isContentBuiltIn() {
426 return myBuildInActions
;
429 private static class LightProcessHandler
extends ProcessHandler
{
430 protected void destroyProcessImpl() {
431 throw new UnsupportedOperationException();
434 protected void detachProcessImpl() {
435 throw new UnsupportedOperationException();
438 public boolean detachIsDefault() {
443 public OutputStream
getProcessInput() {
448 protected class ReaderThread
implements Runnable
{
449 private BufferedReader myReader
;
450 private boolean myRunning
= false;
452 public ReaderThread(@Nullable Reader reader
) {
453 myReader
= reader
!= null ?
new BufferedReader(reader
) : null;
457 if (myReader
== null) return;
462 if (myRunning
&& myReader
!= null && myReader
.ready()) {
463 addMessage(myReader
.readLine());
469 synchronized (this) {
473 catch (IOException e
) {
476 catch (InterruptedException e
) {
477 Disposer
.dispose(LogConsoleBase
.this);
482 public void startRunning() {
486 public void stopRunning() {
488 synchronized (this) {