@@ -28,6 +28,7 @@ import java.awt.event.MouseEvent
28
28
import java.awt.event.MouseListener
29
29
import java.text.DecimalFormat
30
30
import java.util.ArrayList
31
+ import java.util.regex.Pattern
31
32
import javax.swing.Box
32
33
import javax.swing.DefaultComboBoxModel
33
34
import javax.swing.JCheckBoxMenuItem
@@ -46,6 +47,8 @@ import javax.swing.JTable
46
47
import javax.swing.SwingConstants
47
48
import javax.swing.UIManager
48
49
import javax.swing.border.EmptyBorder
50
+ import javax.swing.event.HyperlinkEvent
51
+ import javax.swing.event.HyperlinkListener
49
52
import javax.swing.event.ListSelectionEvent
50
53
import javax.swing.event.ListSelectionListener
51
54
import javax.swing.plaf.basic.BasicProgressBarUI
@@ -63,7 +66,7 @@ import org.utplsql.sqldev.resources.UtplsqlResources
63
66
import org.utplsql.sqldev.runner.UtplsqlRunner
64
67
import org.utplsql.sqldev.runner.UtplsqlWorksheetRunner
65
68
66
- class RunnerPanel implements ActionListener , MouseListener {
69
+ class RunnerPanel implements ActionListener , MouseListener , HyperlinkListener {
67
70
static val GREEN = new Color (0 , 153 , 0 )
68
71
static val RED = new Color (153 , 0 , 0 )
69
72
static val INDICATOR_WIDTH = 20
@@ -103,10 +106,10 @@ class RunnerPanel implements ActionListener, MouseListener {
103
106
RunnerTextField testStartTextField
104
107
FailuresTableModel failuresTableModel
105
108
JTable failuresTable
106
- RunnerTextArea testFailureMessageTextArea
107
- RunnerTextArea testErrorStackTextArea
108
- RunnerTextArea testWarningsTextArea
109
- RunnerTextArea testServerOutputTextArea
109
+ RunnerTextPane testFailureMessageTextPane
110
+ RunnerTextPane testErrorStackTextPane
111
+ RunnerTextPane testWarningsTextPane
112
+ RunnerTextPane testServerOutputTextPane
110
113
JTabbedPane testDetailTabbedPane
111
114
112
115
def Component getGUI () {
@@ -131,10 +134,10 @@ class RunnerPanel implements ActionListener, MouseListener {
131
134
testStartTextField. text = null
132
135
failuresTableModel. model = null
133
136
failuresTableModel. fireTableDataChanged
134
- testFailureMessageTextArea . text = null
135
- testErrorStackTextArea . text = null
136
- testWarningsTextArea . text = null
137
- testServerOutputTextArea . text = null
137
+ testFailureMessageTextPane . text = null
138
+ testErrorStackTextPane . text = null
139
+ testWarningsTextPane . text = null
140
+ testServerOutputTextPane . text = null
138
141
}
139
142
140
143
private def refreshRunsComboBox () {
@@ -216,6 +219,39 @@ class RunnerPanel implements ActionListener, MouseListener {
216
219
}
217
220
}
218
221
222
+ private def getHtml (String text ) {
223
+ val html = ' ' '
224
+ <html>
225
+ <head>
226
+ <style type="text/css">
227
+ body, p {font-family: «testOwnerTextField.font.family»; font-size: 1.0em; line-height: 1.1em; margin-top: 0px; margin-bottom: 0px;}
228
+ </style>
229
+ </head>
230
+ <body>
231
+ «getLinkedText(text)»
232
+ </body>
233
+ </html>
234
+ ' ' '
235
+ return html
236
+ }
237
+
238
+ private def openLink (String link ) {
239
+ val parts = link. split(" /" )
240
+ val ownerName = parts. get(0 )
241
+ val objectName = parts. get(1 )
242
+ var line = Integer . parseInt(parts. get(2 ))
243
+ val dao = new UtplsqlDao (Connections . instance. getConnection(currentRun. connectionName))
244
+ val objectType = dao. getObjectType(ownerName, objectName)
245
+ val fixedObjectType = ' ' ' «objectType»«IF objectType == "PACKAGE" || objectType == "TYPE"» BODY«ENDIF»' ' '
246
+ if (parts. size == 4 ) {
247
+ val procedureName = parts. get(3 )
248
+ val source = dao. getSource(ownerName, fixedObjectType, objectName). trim
249
+ val parser = new UtplsqlParser (source)
250
+ line = parser. getLineOf(procedureName)
251
+ }
252
+ openEditor(ownerName, ' ' ' «objectType»«IF objectType == "PACKAGE" || objectType == "TYPE"» BODY«ENDIF»' ' ' , objectName. toUpperCase, line, 1 )
253
+ }
254
+
219
255
private def openEditor (String owner , String type , String name , int line , int col ) {
220
256
var drillLink = new DefaultDrillLink
221
257
drillLink. connName = currentRun. connectionName
@@ -442,6 +478,13 @@ class RunnerPanel implements ActionListener, MouseListener {
442
478
override mouseReleased (MouseEvent e ) {
443
479
}
444
480
481
+ override hyperlinkUpdate (HyperlinkEvent e ) {
482
+ if (e. eventType == HyperlinkEvent . EventType . ACTIVATED ) {
483
+ val link = e. description
484
+ openLink(link)
485
+ }
486
+ }
487
+
445
488
private static def formatDateTime (String dateTime ) {
446
489
if (dateTime == = null ) {
447
490
return null
@@ -474,19 +517,53 @@ class RunnerPanel implements ActionListener, MouseListener {
474
517
p. testStartTextField. text = formatDateTime(test. startTime)
475
518
p. failuresTableModel. model = test. failedExpectations
476
519
p. failuresTableModel. fireTableDataChanged
477
- p. testFailureMessageTextArea . text = null
520
+ p. testFailureMessageTextPane . text = null
478
521
if (test. failedExpectations !== null && test. failedExpectations. size > 0 ) {
479
522
p. failuresTable. setRowSelectionInterval(0 , 0 )
480
523
}
481
- p. testErrorStackTextArea . text = test. errorStack? . trim
482
- p. testWarningsTextArea . text = test. warnings? . trim
483
- p. testServerOutputTextArea . text = test. serverOutput? . trim
524
+ p. testErrorStackTextPane . text = p . getHtml( test. errorStack? . trim)
525
+ p. testWarningsTextPane . text = p . getHtml( test. warnings? . trim)
526
+ p. testServerOutputTextPane . text = p . getHtml( test. serverOutput? . trim)
484
527
p. syncDetailTab
485
528
p. testOverviewRunMenuItem. enabled = true
486
529
p. testOverviewRunWorksheetMenuItem. enabled = true
487
530
}
488
531
}
489
532
}
533
+
534
+ private def getLinkedText (String text ) {
535
+ if (text == = null ) {
536
+ return " "
537
+ }
538
+ // Patterns (primarily Asserts, Errors, ServerOutput):
539
+ // at "OWNER.PACKAGE.PROCEDURE", line 42
540
+ // at "OWNER.PROCEDURE", line 42
541
+ val p1 = Pattern . compile(' ' ' \s +("([^\. ]+)\. ([^\. "]+)(?:\. ([^\" ]+))?",\s +line\s +([0-9]+))' ' ' )
542
+ var localText = text
543
+ var m = p1. matcher(localText)
544
+ while (m. find) {
545
+ val link = ' ' ' <a href="«m.group(2)»/«m.group(3)»/«m.group(5)»">«m.group(1)»</a>' ' '
546
+ localText = localText. replaceFirst(p1. pattern, link)
547
+ m = p1. matcher(localText)
548
+ }
549
+ // Patterns (primarily Warnings, without line reference, calculate when opening link):
550
+ // owner.package.procedure
551
+ val p2 = Pattern . compile(' ' ' ^\s {2}(([^\. ]+)\. ([^\. ]+)\. (.+))$' ' ' , Pattern . MULTILINE )
552
+ m = p2. matcher(localText)
553
+ while (m. find) {
554
+ val link = ' ' ' <a href="«m.group(2).toUpperCase»/«m.group(3).toUpperCase»/1/«m.group(4).toUpperCase»">«m.group(1)»</a>' ' '
555
+ val start = m. start(0 )
556
+ val end = m. end(0 )
557
+ localText = ' ' ' «localText.substring(0, start)»«link»«localText.substring(end)»' ' '
558
+ m = p2. matcher(localText)
559
+ }
560
+ val result = ' ' '
561
+ «FOR p : localText.split(System.lineSeparator)»
562
+ <p>«p»</p>
563
+ «ENDFOR»
564
+ ' ' '
565
+ return result
566
+ }
490
567
491
568
static class FailuresRowListener implements ListSelectionListener {
492
569
RunnerPanel p
@@ -500,7 +577,9 @@ class RunnerPanel implements ActionListener, MouseListener {
500
577
if (rowIndex != - 1 ) {
501
578
val row = p. failuresTable. convertRowIndexToModel(rowIndex)
502
579
val expectation = p. failuresTableModel. getExpectation(row)
503
- p. testFailureMessageTextArea. text = expectation. failureText
580
+ val html = p. getHtml(expectation. failureText)
581
+ p. testFailureMessageTextPane. text = html
582
+
504
583
}
505
584
}
506
585
}
@@ -958,12 +1037,12 @@ class RunnerPanel implements ActionListener, MouseListener {
958
1037
failuresDescription. headerRenderer = failuresTableHeaderRenderer
959
1038
val failuresTableScrollPane = new JScrollPane (failuresTable)
960
1039
// - failures details
961
- testFailureMessageTextArea = new RunnerTextArea
962
- testFailureMessageTextArea . editable = false
963
- testFailureMessageTextArea . enabled = true
964
- testFailureMessageTextArea . lineWrap = true
965
- testFailureMessageTextArea . wrapStyleWord = true
966
- val testFailureMessageScrollPane = new JScrollPane (testFailureMessageTextArea )
1040
+ testFailureMessageTextPane = new RunnerTextPane
1041
+ testFailureMessageTextPane . editable = false
1042
+ testFailureMessageTextPane . enabled = true
1043
+ testFailureMessageTextPane . contentType = " text/html "
1044
+ testFailureMessageTextPane . addHyperlinkListener( this )
1045
+ val testFailureMessageScrollPane = new JScrollPane (testFailureMessageTextPane )
967
1046
c. gridx = 1
968
1047
c. gridy = 0
969
1048
c. gridwidth = 1
@@ -981,12 +1060,12 @@ class RunnerPanel implements ActionListener, MouseListener {
981
1060
// Errors tabbed pane (Error Stack)
982
1061
val testErrorStackPanel = new JPanel
983
1062
testErrorStackPanel. setLayout(new GridBagLayout ())
984
- testErrorStackTextArea = new RunnerTextArea
985
- testErrorStackTextArea . editable = false
986
- testErrorStackTextArea . enabled = true
987
- testErrorStackTextArea . lineWrap = true
988
- testErrorStackTextArea . wrapStyleWord = true
989
- val testErrorStackScrollPane = new JScrollPane (testErrorStackTextArea )
1063
+ testErrorStackTextPane = new RunnerTextPane
1064
+ testErrorStackTextPane . editable = false
1065
+ testErrorStackTextPane . enabled = true
1066
+ testErrorStackTextPane . contentType = " text/html "
1067
+ testErrorStackTextPane . addHyperlinkListener( this )
1068
+ val testErrorStackScrollPane = new JScrollPane (testErrorStackTextPane )
990
1069
c. gridx = 0
991
1070
c. gridy = 0
992
1071
c. gridwidth = 1
@@ -1001,12 +1080,12 @@ class RunnerPanel implements ActionListener, MouseListener {
1001
1080
// Warnings tabbed pane
1002
1081
val testWarningsPanel = new JPanel
1003
1082
testWarningsPanel. setLayout(new GridBagLayout ())
1004
- testWarningsTextArea = new RunnerTextArea
1005
- testWarningsTextArea . editable = false
1006
- testWarningsTextArea . enabled = true
1007
- testWarningsTextArea . lineWrap = true
1008
- testWarningsTextArea . wrapStyleWord = true
1009
- val testWarningsScrollPane = new JScrollPane (testWarningsTextArea )
1083
+ testWarningsTextPane = new RunnerTextPane
1084
+ testWarningsTextPane . editable = false
1085
+ testWarningsTextPane . enabled = true
1086
+ testWarningsTextPane . contentType = " text/html "
1087
+ testWarningsTextPane . addHyperlinkListener( this )
1088
+ val testWarningsScrollPane = new JScrollPane (testWarningsTextPane )
1010
1089
c. gridx = 0
1011
1090
c. gridy = 0
1012
1091
c. gridwidth = 1
@@ -1021,12 +1100,12 @@ class RunnerPanel implements ActionListener, MouseListener {
1021
1100
// Info tabbed pane (Server Output)
1022
1101
val testServerOutputPanel = new JPanel
1023
1102
testServerOutputPanel. setLayout(new GridBagLayout ())
1024
- testServerOutputTextArea = new RunnerTextArea
1025
- testServerOutputTextArea . editable = false
1026
- testServerOutputTextArea . enabled = true
1027
- testServerOutputTextArea . lineWrap = true
1028
- testServerOutputTextArea . wrapStyleWord = true
1029
- val testServerOutputScrollPane = new JScrollPane (testServerOutputTextArea )
1103
+ testServerOutputTextPane = new RunnerTextPane
1104
+ testServerOutputTextPane . editable = false
1105
+ testServerOutputTextPane . enabled = true
1106
+ testServerOutputTextPane . contentType = " text/html "
1107
+ testServerOutputTextPane . addHyperlinkListener( this )
1108
+ val testServerOutputScrollPane = new JScrollPane (testServerOutputTextPane )
1030
1109
c. gridx = 0
1031
1110
c. gridy = 0
1032
1111
c. gridwidth = 1
@@ -1062,5 +1141,6 @@ class RunnerPanel implements ActionListener, MouseListener {
1062
1141
val referenceBorder = testOwnerTextField. border
1063
1142
testDescriptionTextArea. border = referenceBorder
1064
1143
testIdTextArea. border = referenceBorder
1065
- }
1144
+ }
1145
+
1066
1146
}
0 commit comments