This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch GROOVY_2_5_X
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/GROOVY_2_5_X by this push:
     new e0cf78d  GROOVY-8180 Support Log To File for Groovy Console (closes 
#886)
e0cf78d is described below

commit e0cf78d449a87b8ce785a4df4b5b8829f5ca346c
Author: tttao <big...@gmail.com>
AuthorDate: Sun Mar 3 17:18:52 2019 +0100

    GROOVY-8180 Support Log To File for Groovy Console (closes #886)
---
 .../src/main/groovy/groovy/ui/Console.groovy       | 98 +++++++++++++++-------
 .../groovy/groovy/ui/ConsolePreferences.groovy     | 44 ++++++++--
 .../main/resources/groovy/ui/Console.properties    |  3 +
 3 files changed, 106 insertions(+), 39 deletions(-)

diff --git 
a/subprojects/groovy-console/src/main/groovy/groovy/ui/Console.groovy 
b/subprojects/groovy-console/src/main/groovy/groovy/ui/Console.groovy
index 0d42d74..24f57ea 100644
--- a/subprojects/groovy-console/src/main/groovy/groovy/ui/Console.groovy
+++ b/subprojects/groovy-console/src/main/groovy/groovy/ui/Console.groovy
@@ -49,6 +49,7 @@ import javax.swing.event.HyperlinkEvent
 import javax.swing.event.HyperlinkListener
 import javax.swing.filechooser.FileFilter
 import javax.swing.text.AttributeSet
+import javax.swing.text.Document
 import javax.swing.text.Element
 import javax.swing.text.SimpleAttributeSet
 import javax.swing.text.Style
@@ -132,6 +133,9 @@ class Console implements CaretListener, HyperlinkListener, 
ComponentListener, Fo
     // Maximum number of characters to show on console at any time
     int maxOutputChars = 
System.getProperty('groovy.console.output.limit','20000') as int
 
+    // File to output stdout & stderr, in addition to console
+    PrintWriter outputPrintWriter = null
+
     // UI
     SwingBuilder swing
     RootPaneContainer frame
@@ -280,6 +284,30 @@ class Console implements CaretListener, HyperlinkListener, 
ComponentListener, Fo
         consolePreferences.show()
     }
 
+    void setOutputPreferences(boolean useOutputFile, File outputFile) {
+        if (!useOutputFile) {
+            closeOutputPrintWriter(outputFile)
+        } else {
+            if (outputFile != null) {
+                closeOutputPrintWriter()
+                createOutputPrintWriter(outputFile)
+            }
+        }
+    }
+
+    void createOutputPrintWriter(File outputFile) {
+        outputPrintWriter = new PrintWriter(new FileOutputStream(
+                outputFile,
+                true))
+    }
+
+    void closeOutputPrintWriter() {
+        if (outputPrintWriter != null) {
+            outputPrintWriter.close()
+            outputPrintWriter = null
+        }
+    }
+
     Console() {
         this(new Binding())
     }
@@ -449,7 +477,7 @@ class Console implements CaretListener, HyperlinkListener, 
ComponentListener, Fo
     // Ensure we don't have too much in console (takes too much memory)
     private ensureNoDocLengthOverflow(doc) {
         // if it is a case of stackOverFlowError, show the exception details 
from the front
-        // as there is no point in showing the repeating details at the back 
+        // as there is no point in showing the repeating details at the back
         int offset = stackOverFlowError ? maxOutputChars : 0
         if (doc.length > maxOutputChars) {
             doc.remove(offset, doc.length - maxOutputChars)
@@ -459,7 +487,7 @@ class Console implements CaretListener, HyperlinkListener, 
ComponentListener, Fo
     // Append a string to the output area
     void appendOutput(String text, AttributeSet style){
         def doc = outputArea.styledDocument
-        doc.insertString(doc.length, text, style)
+        insertString(doc, doc.length, text, style)
         ensureNoDocLengthOverflow(doc)
     }
 
@@ -514,23 +542,33 @@ class Console implements CaretListener, 
HyperlinkListener, ComponentListener, Fo
                 hrefAttr.addAttribute(HTML.Attribute.HREF, 'file://' + 
fileNameAndLineNumber)
                 style.addAttribute(HTML.Tag.A, hrefAttr);
 
-                doc.insertString(initialLength,                     
line[0..<index],                    stacktraceStyle)
-                doc.insertString(initialLength + index,             
line[index..<(index + length)],     style)
-                doc.insertString(initialLength + index + length,    
line[(index + length)..-1] + '\n',  stacktraceStyle)
+                insertString(doc, initialLength,                     
line[0..<index],                    stacktraceStyle)
+                insertString(doc, initialLength + index,             
line[index..<(index + length)],     style)
+                insertString(doc, initialLength + index + length,    
line[(index + length)..-1] + '\n',  stacktraceStyle)
             } else {
-                doc.insertString(initialLength, line + '\n', stacktraceStyle)
+                insertString(doc, initialLength, line + '\n', stacktraceStyle)
             }
         }
 
         ensureNoDocLengthOverflow(doc)
     }
 
+    void insertString(Document doc, int offset, String text, AttributeSet 
attributeSet) {
+        doc.insertString(offset, text, attributeSet)
+
+        // Output to file if activated
+        if (outputPrintWriter != null) {
+            outputPrintWriter.append(text)
+            outputPrintWriter.flush()
+        }
+    }
+
     // Append a string to the output area on a new line
     void appendOutputNl(text, style) {
         def doc = outputArea.styledDocument
         def len = doc.length
         def alreadyNewLine = (len == 0 || doc.getText(len - 1, 1) == '\n')
-        doc.insertString(doc.length, ' \n', style)
+        insertString(doc, doc.length, ' \n', style)
         if (alreadyNewLine) {
             doc.remove(len, 2) // windows hack to fix (improve?) line spacing
         }
@@ -541,7 +579,7 @@ class Console implements CaretListener, HyperlinkListener, 
ComponentListener, Fo
         appendOutput(text, style)
         def doc = outputArea.styledDocument
         def len = doc.length
-        doc.insertString(len, ' \n', style)
+        insertString(doc, len, ' \n', style)
         doc.remove(len, 2) // windows hack to fix (improve?) line spacing
     }
 
@@ -583,7 +621,7 @@ class Console implements CaretListener, HyperlinkListener, 
ComponentListener, Fo
         captureStdErr = evt.source.selected
         prefs.putBoolean('captureStdErr', captureStdErr)
     }
-    
+
     void fullStackTraces(EventObject evt) {
         fullStackTraces = evt.source.selected
         System.setProperty('groovy.full.stacktrace',
@@ -718,7 +756,7 @@ class Console implements CaretListener, HyperlinkListener, 
ComponentListener, Fo
         consoleController.systemOutInterceptor = systemOutInterceptor
         consoleController.systemErrorInterceptor = systemErrorInterceptor
         SwingBuilder swing = new SwingBuilder()
-        consoleController.swing = swing 
+        consoleController.swing = swing
         frameConsoleDelegates.each {k, v -> swing[k] = v}
         swing.controller = consoleController
         swing.build(ConsoleActions)
@@ -809,7 +847,7 @@ class Console implements CaretListener, HyperlinkListener, 
ComponentListener, Fo
                     int errorLine = se.line
                     String message = se.originalMessage
 
-                    String scriptFileName = scriptFile?.name ?: 
DEFAULT_SCRIPT_NAME_START 
+                    String scriptFileName = scriptFile?.name ?: 
DEFAULT_SCRIPT_NAME_START
 
                     def doc = outputArea.styledDocument
 
@@ -819,15 +857,15 @@ class Console implements CaretListener, 
HyperlinkListener, ComponentListener, Fo
                     hrefAttr.addAttribute(HTML.Attribute.HREF, 'file://' + 
scriptFileName + ':' + errorLine)
                     style.addAttribute(HTML.Tag.A, hrefAttr);
 
-                    doc.insertString(doc.length, message + ' at ', 
stacktraceStyle)
-                    doc.insertString(doc.length, "line: ${se.line}, column: 
${se.startColumn}\n\n", style)
+                    insertString(doc, doc.length, message + ' at ', 
stacktraceStyle)
+                    insertString(doc, doc.length, "line: ${se.line}, column: 
${se.startColumn}\n\n", style)
                 } else if (error instanceof Throwable) {
                     reportException(error)
                 } else if (error instanceof ExceptionMessage) {
-                    reportException(error.cause) 
+                    reportException(error.cause)
                 } else if (error instanceof SimpleMessage) {
                     def doc = outputArea.styledDocument
-                    doc.insertString(doc.length, "${error.message}\n", new 
SimpleAttributeSet())
+                    insertString(doc, doc.length, "${error.message}\n", new 
SimpleAttributeSet())
                 }
             }
         } else {
@@ -839,7 +877,7 @@ class Console implements CaretListener, HyperlinkListener, 
ComponentListener, Fo
         }
 
         // GROOVY-4496: set the output window position to the top-left so the 
exception details are visible from the start
-        outputArea.caretPosition = 0 
+        outputArea.caretPosition = 0
 
         if (detachedOutput) {
             prepareOutputWindow()
@@ -880,7 +918,7 @@ class Console implements CaretListener, HyperlinkListener, 
ComponentListener, Fo
             showOutputWindow()
         }
     }
-    
+
     def compileFinishNormal() {
         statusLabel.text = 'Compilation complete.'
     }
@@ -1173,7 +1211,7 @@ class Console implements CaretListener, 
HyperlinkListener, ComponentListener, Fo
                     // set the flag that will be used in printing exception 
details in output pane
                     stackOverFlowError = true
                     clearOutput()
-                } 
+                }
                 SwingUtilities.invokeLater { finishException(t, true) }
             } finally {
                 runThread = null
@@ -1219,7 +1257,7 @@ class Console implements CaretListener, 
HyperlinkListener, ComponentListener, Fo
             }
         }
     }
-    
+
     def selectFilename(name = 'Open') {
         def fc = new JFileChooser(currentFileChooserDir)
         fc.fileSelectionMode = JFileChooser.FILES_ONLY
@@ -1305,21 +1343,21 @@ class Console implements CaretListener, 
HyperlinkListener, ComponentListener, Fo
     void replace(EventObject evt = null) {
         FindReplaceUtility.showDialog(true)
     }
-    
+
     void comment(EventObject evt = null) {
        def rootElement = inputArea.document.defaultRootElement
        def cursorPos = inputArea.getCaretPosition()
        int startRow = rootElement.getElementIndex(cursorPos)
        int endRow = startRow
-       
+
        if (inputArea.getSelectedText()) {
            def selectionStart = inputArea.getSelectionStart()
            startRow = rootElement.getElementIndex(selectionStart)
            def selectionEnd = inputArea.getSelectionEnd()
            endRow = rootElement.getElementIndex(selectionEnd)
        }
-       
-       // If multiple commented lines intermix with uncommented lines, 
consider them uncommented 
+
+       // If multiple commented lines intermix with uncommented lines, 
consider them uncommented
        def allCommented = true
        startRow.upto(endRow) { rowIndex ->
            def rowElement = rootElement.getElement(rowIndex)
@@ -1330,7 +1368,7 @@ class Console implements CaretListener, 
HyperlinkListener, ComponentListener, Fo
                allCommented = false
            }
        }
-       
+
        startRow.upto(endRow) { rowIndex ->
            def rowElement = rootElement.getElement(rowIndex)
            int startOffset = rowElement.getStartOffset()
@@ -1345,7 +1383,7 @@ class Console implements CaretListener, 
HyperlinkListener, ComponentListener, Fo
                inputArea.document.insertString(startOffset, "//", new 
SimpleAttributeSet())
            }
        }
-       
+
     }
 
     void selectBlock(EventObject evt = null) {
@@ -1427,7 +1465,7 @@ class Console implements CaretListener, 
HyperlinkListener, ComponentListener, Fo
     void showCompilingMessage() {
         statusLabel.text = 'Script compiling now. Please wait.'
     }
-    
+
     // Shows the detached 'outputArea' dialog
     void showOutputWindow(EventObject evt = null) {
         if (detachedOutput) {
@@ -1471,7 +1509,7 @@ class Console implements CaretListener, 
HyperlinkListener, ComponentListener, Fo
         } else if (newFontSize < 4) {
             newFontSize = 4
         }
-        
+
         prefs.putInt('fontSize', newFontSize)
 
         // don't worry, the fonts won't be changed to this family, the styles 
will only derive from this
@@ -1575,16 +1613,16 @@ class Console implements CaretListener, 
HyperlinkListener, ComponentListener, Fo
         }
     }
 
-    public void componentShown(ComponentEvent e) { }
+    void componentShown(ComponentEvent e) { }
 
-    public void focusGained(FocusEvent e) {
+    void focusGained(FocusEvent e) {
         // remember component with focus for text-copy functionality
         if (e.component == outputArea || e.component == inputArea) {
             copyFromComponent = e.component
         }
     }
 
-    public void focusLost(FocusEvent e) { }
+    void focusLost(FocusEvent e) { }
 
     private static boolean isWindows() {
         return getOsName().startsWith("windows")
diff --git 
a/subprojects/groovy-console/src/main/groovy/groovy/ui/ConsolePreferences.groovy
 
b/subprojects/groovy-console/src/main/groovy/groovy/ui/ConsolePreferences.groovy
index 2bc00c7..6e804b8 100644
--- 
a/subprojects/groovy-console/src/main/groovy/groovy/ui/ConsolePreferences.groovy
+++ 
b/subprojects/groovy-console/src/main/groovy/groovy/ui/ConsolePreferences.groovy
@@ -36,6 +36,7 @@ class ConsolePreferences {
     private final MessageSource T
 
     private JDialog dialog
+    File outputFile
 
     ConsolePreferences(console) {
         this.console = console
@@ -63,14 +64,26 @@ class ConsolePreferences {
                 title: T['prefs.dialog.title'], owner: console.frame, modal: 
true
         ) {
             vbox {
-                hbox(border: titledBorder(T['prefs.output.settings.title'])) {
-                    label "${T['prefs.max.characters.output']}:"
-
-                    formattedTextField value: maxOutputChars, id: 
'txtMaxOutputChars',
-                            text:
-                                    bind(target: this, targetProperty: 
'maxOutputChars',
-                                            validator: this.&isInteger, 
converter: Integer.&parseInt),
-                            columns: 6
+                vbox(border: titledBorder(T['prefs.output.settings.title'])) {
+                    hbox {
+                        label "${T['prefs.max.characters.output']}:"
+
+                        formattedTextField value: maxOutputChars, id: 
'txtMaxOutputChars',
+                                text:
+                                        bind(target: this, targetProperty: 
'maxOutputChars',
+                                                validator: this.&isInteger, 
converter: Integer.&parseInt),
+                                columns: 6
+                    }
+
+                    hbox {
+                        checkBox T['prefs.output.file'], id: 
'outputFileCheckBox', selected: false
+                        hglue()
+                        label T['prefs.output.file.name'], id: 
'outputFileName',
+                                enabled: bind(source: outputFileCheckBox, 
sourceProperty: 'selected')
+                        button T['prefs.output.file.select'], id: 
'outputFileNameButton',
+                                enabled: bind(source: outputFileCheckBox, 
sourceProperty: 'selected'),
+                                actionPerformed: this.&onChooseFile
+                    }
                 }
 
                 vglue()
@@ -80,10 +93,11 @@ class ConsolePreferences {
                     hglue()
                     button T['prefs.close'], id: 'closePrefsButton', 
actionPerformed: this.&onClose
                 }
+
             }
         }
 
-        console.swing.txtMaxOutputChars.maximumSize=new 
Dimension(Integer.MAX_VALUE, (int) 
console.swing.txtMaxOutputChars.preferredSize.height)
+        console.swing.txtMaxOutputChars.maximumSize = new 
Dimension(Integer.MAX_VALUE, (int) 
console.swing.txtMaxOutputChars.preferredSize.height)
     }
 
     private boolean isInteger(value) {
@@ -106,9 +120,21 @@ class ConsolePreferences {
         if (maxOutputChars != console.maxOutputChars) {
             console.maxOutputChars = maxOutputChars
         }
+
+        console.setOutputPreferences(console.swing.outputFileCheckBox.enabled, 
outputFile)
+
         dialog.dispose()
     }
 
+    private void onChooseFile(EventObject event) {
+        JFileChooser fileChooser = console.swing.fileChooser()
+
+        if (fileChooser.showOpenDialog(dialog) == JFileChooser.APPROVE_OPTION) 
{
+            outputFile = fileChooser.selectedFile
+        }
+        console.swing.outputFileName.text = outputFile.path
+    }
+
     // Useful for testing gui
     static void main(args) {
         
javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName())
diff --git 
a/subprojects/groovy-console/src/main/resources/groovy/ui/Console.properties 
b/subprojects/groovy-console/src/main/resources/groovy/ui/Console.properties
index 4574a51..1ca57f8 100644
--- a/subprojects/groovy-console/src/main/resources/groovy/ui/Console.properties
+++ b/subprojects/groovy-console/src/main/resources/groovy/ui/Console.properties
@@ -41,4 +41,7 @@ prefs.dialog.title=Preferences
 prefs.output.settings.title=Output Settings
 prefs.max.characters.output=Maximum Characters to Output
 prefs.reset.defaults=Reset To Defaults
+prefs.output.file=Output to file
+prefs.output.file.select=Select
+prefs.output.file.name=<Output file name>
 prefs.close=Close

Reply via email to