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
;
42 import javax
.swing
.event
.ChangeEvent
;
44 import java
.awt
.event
.ActionEvent
;
45 import java
.awt
.event
.ActionListener
;
46 import java
.awt
.event
.KeyEvent
;
48 import java
.util
.List
;
51 * Created by IntelliJ IDEA.
52 * User: Eugene.Kudelevsky
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());
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
);
105 private static Reader
getReader(Reader reader
, long skippedContents
) {
106 reader
= new BufferedReader(reader
);
108 reader
.skip(skippedContents
);
110 catch (IOException e
) {
117 private static Reader
getReader(File file
, long skippedContents
) {
118 Reader reader
= null;
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
) {
139 public void setFilterModel(LogFilterModel model
) {
140 if (myModel
!= null) {
141 myModel
.removeFilterListener(this);
144 myModel
.addFilterListener(this);
147 public LogFilterModel
getFilterModel() {
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();
164 myFilter
.setSelectedItem(customFilter
!= null ? customFilter
: "");
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
) {
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);
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
);
232 public JComponent
getComponent() {
233 if (!myWasInitialized
) {
234 myWasInitialized
= true;
235 add(myConsole
.getComponent(), BorderLayout
.CENTER
);
236 add(createToolbar(), BorderLayout
.NORTH
);
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
) {
259 public String
getTabTitle() {
263 public void dispose() {
264 myModel
.removeFilterListener(this);
265 if (myReaderThread
!= null && myReaderThread
.myReader
!= null) {
266 myReaderThread
.stopRunning();
268 myReaderThread
.myReader
.close();
270 catch (IOException e
) {
273 myReaderThread
.myReader
= null;
274 myReaderThread
= null;
276 if (myConsole
!= null) {
277 Disposer
.dispose(myConsole
);
280 if (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());
306 if (myModel
.isApplicable(text
)) {
307 Key key
= myModel
.processLine(text
);
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);
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
;
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
;
362 final String
[] lines
= myOriginalDocument
.toString().split("\n");
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;
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
);
395 ConsoleViewContentType type
= ConsoleViewContentType
.getConsoleViewType(key
);
397 myConsole
.print(line
+ "\n", type
);
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() {
424 public synchronized void 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
);
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() {
471 public OutputStream
getProcessInput() {
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;
485 if (myReader
== null) return;
490 if (myRunning
&& myReader
!= null && myReader
.ready()) {
491 addMessage(myReader
.readLine());
497 synchronized (this) {
501 catch (IOException e
) {
504 catch (InterruptedException e
) {
505 Disposer
.dispose(LogConsoleBase
.this);
510 public void startRunning() {
514 public void stopRunning() {
516 synchronized (this) {