View Javadoc
1   package com.jsql.view.swing.panel.consoles;
2   
3   import com.jsql.model.bean.util.HttpHeader;
4   import com.jsql.model.injection.vendor.model.VendorYaml;
5   import com.jsql.util.I18nUtil;
6   import com.jsql.util.LogLevelUtil;
7   import com.jsql.util.StringUtil;
8   import com.jsql.view.swing.panel.util.HTMLEditorKitTextPaneWrap;
9   import com.jsql.view.swing.tab.TabbedPaneWheeled;
10  import com.jsql.view.swing.text.JPopupTextComponent;
11  import com.jsql.view.swing.text.JTextPanePlaceholder;
12  import com.jsql.view.swing.text.SyntaxTextArea;
13  import com.jsql.view.swing.util.I18nViewUtil;
14  import com.jsql.view.swing.util.MediatorHelper;
15  import com.jsql.view.swing.util.UiUtil;
16  import org.apache.commons.lang3.StringUtils;
17  import org.apache.logging.log4j.LogManager;
18  import org.apache.logging.log4j.Logger;
19  import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
20  import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
21  import org.fife.ui.rtextarea.RTextScrollPane;
22  import org.jsoup.Jsoup;
23  import org.jsoup.safety.Safelist;
24  
25  import javax.swing.*;
26  import javax.swing.text.DefaultCaret;
27  import java.awt.*;
28  import java.awt.event.FocusAdapter;
29  import java.awt.event.FocusEvent;
30  import java.util.AbstractMap.SimpleEntry;
31  import java.util.Arrays;
32  import java.util.stream.Stream;
33  
34  public class TabbedPaneNetworkTab extends TabbedPaneWheeled {
35      
36      /**
37       * Log4j logger sent to view.
38       */
39      private static final Logger LOGGER = LogManager.getRootLogger();
40  
41      private final RSyntaxTextArea textAreaUrl = new SyntaxTextArea(I18nUtil.valueByKey("NETWORK_LINE_PLACEHOLDER_URL"));
42      private final RSyntaxTextArea textAreaResponse = new SyntaxTextArea(I18nUtil.valueByKey("NETWORK_LINE_PLACEHOLDER_RESPONSE"));
43      private final RSyntaxTextArea textAreaSource = new SyntaxTextArea(I18nUtil.valueByKey("NETWORK_LINE_PLACEHOLDER_SOURCE"));
44      private final JTextPane textPanePreview = new JPopupTextComponent<>(new JTextPanePlaceholder(I18nUtil.valueByKey("NETWORK_LINE_PLACEHOLDER_PREVIEW")){
45          @Override
46          public boolean isEditable() {
47              return false;
48          }
49      }).getProxy();
50      private final RSyntaxTextArea textAreaHeader = new SyntaxTextArea(I18nUtil.valueByKey("NETWORK_LINE_PLACEHOLDER_HEADERS"));
51      private final RSyntaxTextArea textAreaRequest = new SyntaxTextArea(I18nUtil.valueByKey("NETWORK_LINE_PLACEHOLDER_REQUEST"));
52      private final JCheckBox checkBoxDecode = new JCheckBox("Decode", MediatorHelper.model().getMediatorUtils().getPreferencesUtil().isUrlDecodeNetworkTab());
53  
54      public TabbedPaneNetworkTab() {
55          this.setName("tabNetwork");
56          var panelDecode = new JPanel(new BorderLayout());
57          panelDecode.add(this.checkBoxDecode, BorderLayout.EAST);  // reduce to minimum size as checkbox expands by the label
58          this.putClientProperty("JTabbedPane.trailingComponent", panelDecode);
59          this.checkBoxDecode.setHorizontalTextPosition(SwingConstants.TRAILING);
60  
61          I18nViewUtil.addComponentForKey("NETWORK_LINE_PLACEHOLDER_URL", this.textAreaUrl);
62          I18nViewUtil.addComponentForKey("NETWORK_LINE_PLACEHOLDER_RESPONSE", this.textAreaResponse);
63          I18nViewUtil.addComponentForKey("NETWORK_LINE_PLACEHOLDER_SOURCE", this.textAreaSource);
64          I18nViewUtil.addComponentForKey("NETWORK_LINE_PLACEHOLDER_PREVIEW", this.textPanePreview);
65          I18nViewUtil.addComponentForKey("NETWORK_LINE_PLACEHOLDER_HEADERS", this.textAreaHeader);
66          I18nViewUtil.addComponentForKey("NETWORK_LINE_PLACEHOLDER_REQUEST", this.textAreaRequest);
67          Stream.of(
68              new SimpleEntry<>("NETWORK_TAB_URL_LABEL", this.textAreaUrl),
69              new SimpleEntry<>("NETWORK_TAB_HEADERS_LABEL", this.textAreaHeader),
70              new SimpleEntry<>("NETWORK_TAB_PARAMS_LABEL", this.textAreaRequest),
71              new SimpleEntry<>("NETWORK_TAB_RESPONSE_LABEL", this.textAreaResponse),
72              new SimpleEntry<>("NETWORK_TAB_SOURCE_LABEL", this.textAreaSource),
73              new SimpleEntry<>("NETWORK_TAB_PREVIEW_LABEL", this.textPanePreview)
74          )
75          .forEach(entry -> {
76              this.addTab(
77                  I18nUtil.valueByKey(entry.getKey()),
78                  entry.getValue() == this.textAreaSource
79                  ? new RTextScrollPane(entry.getValue(), false)
80                  : new JScrollPane(entry.getValue())
81              );
82              var label = new JLabel(I18nUtil.valueByKey(entry.getKey()));
83              label.setName("label"+ entry.getKey());
84              this.setTabComponentAt(
85                  this.indexOfTab(I18nUtil.valueByKey(entry.getKey())),
86                  label
87              );
88              I18nViewUtil.addComponentForKey(entry.getKey(), label);
89  
90              entry.getValue().setName("text"+ entry.getKey());
91  
92              DefaultCaret caret = (DefaultCaret) entry.getValue().getCaret();
93              caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
94          });
95  
96          Arrays.asList(this.textAreaUrl, this.textAreaHeader, this.textAreaRequest, this.textAreaResponse, this.textAreaSource).forEach(entry -> {
97              entry.setEditable(false);
98              entry.setLineWrap(true);
99          });
100         this.textAreaResponse.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_HTML);
101         this.textAreaSource.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_HTML);
102         this.textAreaSource.setHighlightSecondaryLanguages(true);
103         this.applyTheme();
104         
105         this.textPanePreview.setEditorKit(new HTMLEditorKitTextPaneWrap());
106         this.textPanePreview.setContentType("text/html");
107         this.textPanePreview.setEditable(false);
108         this.textPanePreview.getCaret().setBlinkRate(0);
109         this.textPanePreview.addFocusListener(new FocusAdapter() {
110             @Override
111             public void focusGained(FocusEvent focusEvent) {
112                 TabbedPaneNetworkTab.this.textPanePreview.getCaret().setVisible(true);
113                 TabbedPaneNetworkTab.this.textPanePreview.getCaret().setSelectionVisible(true);
114             }
115         });
116     }
117     
118     public void changeTextNetwork(HttpHeader networkData) {
119         this.textAreaRequest.setText(this.checkBoxDecode.isSelected() ? StringUtil.fromUrl(networkData.getPost()) : networkData.getPost());
120         this.textAreaUrl.setText(this.checkBoxDecode.isSelected() ? StringUtil.fromUrl(networkData.getUrl()) : networkData.getUrl());
121 
122         this.textAreaHeader.setText(StringUtils.EMPTY);
123         if (networkData.getHeader() != null) {
124             for (String key: networkData.getHeader().keySet()) {
125                 this.textAreaHeader.append(key + ": " + (this.checkBoxDecode.isSelected() ? StringUtil.fromUrl(networkData.getHeader().get(key)) : networkData.getHeader().get(key)));
126                 this.textAreaHeader.append("\n");
127             }
128         }
129 
130         this.textAreaResponse.setText(StringUtils.EMPTY);
131         if (networkData.getResponse() != null) {
132             for (String key: networkData.getResponse().keySet()) {
133                 this.textAreaResponse.append(key + ": " + (this.checkBoxDecode.isSelected() ? StringUtil.fromUrl(networkData.getResponse().get(key)) : networkData.getResponse().get(key)));
134                 this.textAreaResponse.append("\n");
135             }
136         }
137 
138         // Fix #53736: ArrayIndexOutOfBoundsException on setText()
139         // Fix #54573: NullPointerException on setText()
140         try {
141             this.textAreaSource.setText(
142                 StringUtil.detectUtf8(networkData.getSource())
143                 .replaceAll(VendorYaml.CALIBRATOR_SQL +"{5,}", VendorYaml.CALIBRATOR_SQL +"*")
144                 .trim()
145             );
146         } catch (ArrayIndexOutOfBoundsException | NullPointerException e) {
147             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
148         }
149         
150         this.textPanePreview.getEditorKit().createDefaultDocument();  // Reset EditorKit to disable previous document effect
151         // Proxy is used by jsoup to display <img> tags
152         // Previous test for 2xx Success and 3xx Redirection was Header only, now get the HTML content
153         // Fix #35352: EmptyStackException on setText()
154         // Fix #39841: RuntimeException on setText()
155         // Fix #42523: ExceptionInInitializerError on clean()
156         try {
157             this.textPanePreview.setText(
158                 Jsoup.clean(
159                     String.format(
160                         "<html>%s</html>",
161                         StringUtil.detectUtf8(networkData.getSource())
162                     )
163                     .replaceAll("<img[^>]*>",StringUtils.EMPTY)  // avoid loading external resources
164                     .replaceAll("<input[^>]*type=\"?hidden\"?.*>", StringUtils.EMPTY)
165                     .replaceAll(
166                         "<input[^>]*type=\"?(submit|button)\"?.*>",
167                         "<div style=\"background-color:#eeeeee;text-align:center;border:1px solid black;width:100px;\">button</div>"
168                     )
169                     .replaceAll(
170                         "<input[^>]*>",
171                         "<div style=\"text-align:center;border:1px solid black;width:100px;\">input</div>"
172                     ),
173                     Safelist.relaxed().addTags("center", "div", "span").addAttributes(":all", "style")
174                 )
175             );
176         } catch (Exception | ExceptionInInitializerError e) {
177             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
178         }
179     }
180     
181     public void reset() {
182         this.textAreaUrl.setText(StringUtils.EMPTY);
183         this.textAreaHeader.setText(StringUtils.EMPTY);
184         this.textAreaRequest.setText(StringUtils.EMPTY);
185         this.textAreaResponse.setText(StringUtils.EMPTY);
186         
187         // Fix #54572: NullPointerException on setText()
188         try {
189             this.textAreaSource.setText(StringUtils.EMPTY);
190         } catch (NullPointerException e) {
191             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
192         }
193         
194         // Fix #41879: ArrayIndexOutOfBoundsException on setText()
195         try {
196             this.textPanePreview.setText(StringUtils.EMPTY);
197         } catch (ArrayIndexOutOfBoundsException e) {
198             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
199         }
200     }
201 
202     public void applyTheme() {
203         Arrays.asList(
204             this.textAreaUrl, this.textAreaHeader, this.textAreaRequest, this.textAreaResponse, this.textAreaSource
205         ).forEach(UiUtil::applySyntaxTheme);
206     }
207 
208     public JCheckBox getCheckBoxDecode() {
209         return this.checkBoxDecode;
210     }
211 }