1
2
3
4
5
6
7
8
9
10
11 package com.jsql.view.swing.panel;
12
13 import com.formdev.flatlaf.FlatClientProperties;
14 import com.formdev.flatlaf.icons.FlatRadioButtonMenuItemIcon;
15 import com.jsql.model.injection.method.AbstractMethodInjection;
16 import com.jsql.util.I18nUtil;
17 import com.jsql.util.LogLevelUtil;
18 import com.jsql.util.ParameterUtil;
19 import com.jsql.util.StringUtil;
20 import com.jsql.view.swing.panel.address.ActionEnterAddressBar;
21 import com.jsql.view.swing.panel.address.PanelTrailingAddress;
22 import com.jsql.view.swing.panel.address.ModelAddressLine;
23 import com.jsql.view.swing.panel.util.ButtonExpandText;
24 import com.jsql.view.swing.text.*;
25 import com.jsql.view.swing.text.listener.DocumentListenerEditing;
26 import com.jsql.view.swing.util.I18nViewUtil;
27 import com.jsql.view.swing.util.MediatorHelper;
28 import com.jsql.view.swing.util.RadioItemNonClosing;
29 import com.jsql.view.swing.util.UiUtil;
30 import org.apache.commons.lang3.StringUtils;
31 import org.apache.logging.log4j.LogManager;
32 import org.apache.logging.log4j.Logger;
33
34 import javax.swing.*;
35 import java.awt.*;
36 import java.awt.event.MouseAdapter;
37 import java.awt.event.MouseEvent;
38 import java.util.Arrays;
39 import java.util.concurrent.atomic.AtomicReference;
40 import java.util.function.Supplier;
41 import java.util.stream.Stream;
42
43
44
45
46
47 public class PanelAddressBar extends JPanel {
48
49 private static final Logger LOGGER = LogManager.getRootLogger();
50
51 private final AtomicReference<JTextField> atomicTextFieldAddress = new AtomicReference<>();
52 private final AtomicReference<JTextField> atomicTextFieldRequest = new AtomicReference<>();
53 private final AtomicReference<JTextField> atomicTextFieldHeader = new AtomicReference<>();
54
55 private final AtomicReference<JRadioButton> atomicRadioRequest = new AtomicReference<>();
56 private final AtomicReference<JRadioButton> atomicRadioMethod = new AtomicReference<>();
57 private final AtomicReference<JRadioButton> atomicRadioHeader = new AtomicReference<>();
58
59 private static final String KEY_ADDRESS_BAR_PLACEHOLDER = "ADDRESS_BAR_PLACEHOLDER";
60 private static final String BUTTON_ADVANCED = "BUTTON_ADVANCED";
61
62
63 private AbstractMethodInjection methodInjection = MediatorHelper.model().getMediatorMethod().getQuery();
64 private String typeRequest = StringUtil.GET;
65
66 private final PanelTrailingAddress panelTrailingAddress;
67
68 private boolean isAdvanceActivated = false;
69
70 public PanelAddressBar() {
71 var buttonGroup = new ButtonGroup();
72
73 Stream.of(
74 new ModelAddressLine(
75 "URL",
76 MediatorHelper.model().getMediatorMethod().getQuery(),
77 "QUERYSTRING",
78 this.atomicRadioRequest,
79 I18nUtil.valueByKey(PanelAddressBar.KEY_ADDRESS_BAR_PLACEHOLDER),
80 this.atomicTextFieldAddress
81 ),
82 new ModelAddressLine(
83 StringUtil.GET,
84 MediatorHelper.model().getMediatorMethod().getRequest(),
85 "REQUEST",
86 this.atomicRadioMethod,
87 "e.g. key=value&injectMe=",
88 this.atomicTextFieldRequest
89 ),
90 new ModelAddressLine(
91 "Header",
92 MediatorHelper.model().getMediatorMethod().getHeader(),
93 "HEADER",
94 this.atomicRadioHeader,
95 String.format(
96 "e.g. key: value\\r\\nCookie: cKey1=cValue1; cKey2=cValue2\\r\\n%s: %s %s\\r\\ninjectMe:",
97 "Authorization",
98 "Basic",
99 "dXNlcjpwYXNz"
100 ),
101 this.atomicTextFieldHeader
102 )
103 )
104 .forEach(modelLine -> {
105 var i18nTooltip = String.format("FIELD_%s_TOOLTIP", modelLine.i18n);
106 var tooltipTextfield = new AtomicReference<>(new JToolTipI18n(I18nUtil.valueByKey(i18nTooltip)));
107 modelLine.textfield.set(new JPopupTextField(new JTextFieldPlaceholder(
108 modelLine.placeholder,
109 modelLine.radio == this.atomicRadioRequest ? 18 : 0
110 ) {
111 @Override
112 public JToolTip createToolTip() {
113 return tooltipTextfield.get();
114 }
115 }).getProxy());
116 I18nViewUtil.addComponentForKey(i18nTooltip, tooltipTextfield.get());
117 modelLine.textfield.get().addActionListener(new ActionEnterAddressBar(this));
118 modelLine.textfield.get().setVisible(false);
119 modelLine.textfield.get().setToolTipText(I18nUtil.valueByKey(i18nTooltip));
120
121 var i18nRadio = String.format("METHOD_%s_TOOLTIP", modelLine.i18n);
122 var tooltipRadio = new AtomicReference<>(new JToolTipI18n(I18nUtil.valueByKey(i18nRadio)));
123 modelLine.radio.set(
124 new JRadioButton(modelLine.request) {
125 @Override
126 public JToolTip createToolTip() {
127 return tooltipRadio.get();
128 }
129 }
130 );
131 I18nViewUtil.addComponentForKey(i18nRadio, tooltipRadio.get());
132 modelLine.radio.get().setToolTipText(I18nUtil.valueByKey(i18nRadio));
133 modelLine.radio.get().setSelected(modelLine.radio == this.atomicRadioRequest);
134 modelLine.radio.get().setHorizontalTextPosition(SwingConstants.LEFT);
135 modelLine.radio.get().setVisible(false);
136 modelLine.radio.get().setBorder(BorderFactory.createEmptyBorder(
137 modelLine.radio == this.atomicRadioRequest ? 0 : 6, 3, 0, 3
138 ));
139 modelLine.radio.get().addActionListener(e -> MediatorHelper.panelAddressBar().setMethodInjection(modelLine.method));
140 buttonGroup.add(modelLine.radio.get());
141 });
142
143 this.atomicTextFieldAddress.get().setFont(UiUtil.FONT_NON_MONO_BIG);
144 this.atomicTextFieldAddress.get().setName("textFieldAddress");
145 this.atomicTextFieldAddress.get().setPreferredSize(new Dimension(50, 32));
146 this.atomicTextFieldAddress.get().setVisible(true);
147 I18nViewUtil.addComponentForKey(PanelAddressBar.KEY_ADDRESS_BAR_PLACEHOLDER, this.atomicTextFieldAddress.get());
148
149 this.panelTrailingAddress = new PanelTrailingAddress(this);
150 this.atomicTextFieldAddress.get().putClientProperty(FlatClientProperties.TEXT_FIELD_TRAILING_COMPONENT, this.panelTrailingAddress);
151 this.atomicTextFieldAddress.get().putClientProperty(FlatClientProperties.TEXT_FIELD_LEADING_ICON, UiUtil.GLOBE.getIcon());
152 this.atomicTextFieldRequest.get().putClientProperty(
153 FlatClientProperties.TEXT_FIELD_TRAILING_COMPONENT,
154 new ButtonExpandText(this.atomicTextFieldRequest.get())
155 );
156 this.atomicTextFieldHeader.get().putClientProperty(
157 FlatClientProperties.TEXT_FIELD_TRAILING_COMPONENT,
158 new ButtonExpandText(this.atomicTextFieldHeader.get())
159 );
160
161 this.initLayout();
162 }
163
164 private void initLayout() {
165 final JLabel advancedButton = this.initAdvancedButton();
166
167 this.setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
168
169
170 var panelTextFields = new JPanel();
171 var groupLayout = new GroupLayout(panelTextFields);
172 panelTextFields.setLayout(groupLayout);
173 panelTextFields.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 0));
174 this.add(panelTextFields);
175
176 final var popupMethods = new JPopupMenu();
177 final var buttonGroupMethods = new ButtonGroup();
178
179 for (String method: new String[]{ "DELETE", StringUtil.GET, "HEAD", "OPTIONS", StringUtil.POST, "PUT", "TRACE" }) {
180 final JMenuItem newMenuItem = new RadioItemNonClosing(method, StringUtil.GET.equals(method));
181 newMenuItem.addActionListener(actionEvent -> {
182 this.typeRequest = newMenuItem.getText();
183 this.atomicRadioMethod.get().setText(this.typeRequest);
184 this.atomicRadioMethod.get().requestFocusInWindow();
185 });
186 popupMethods.add(newMenuItem);
187 buttonGroupMethods.add(newMenuItem);
188 }
189
190 var tooltipMethods = new AtomicReference<>(new JToolTipI18n(I18nUtil.valueByKey("METHOD_CUSTOM_TOOLTIP")));
191 var panelCustomMethod = new JPanel(new BorderLayout()) {
192 @Override
193 public JToolTip createToolTip() {
194 return tooltipMethods.get();
195 }
196 };
197 I18nViewUtil.addComponentForKey("METHOD_CUSTOM_TOOLTIP", tooltipMethods.get());
198 Supplier<Color> colorBackground = () -> UIManager.getColor("MenuItem.background");
199 Supplier<Color> colorSelectionBackground = () -> UIManager.getColor("MenuItem.selectionBackground");
200 panelCustomMethod.setBackground(colorBackground.get());
201
202 final var radioCustomMethod = new JRadioButton() {
203 @Override
204 public JToolTip createToolTip() {
205 return tooltipMethods.get();
206 }
207 };
208 radioCustomMethod.setBorder(BorderFactory.createEmptyBorder(0, 6, 0, 0));
209 radioCustomMethod.setIcon(new FlatRadioButtonMenuItemIcon());
210 radioCustomMethod.setBackground(colorBackground.get());
211 buttonGroupMethods.add(radioCustomMethod);
212
213 final JTextField inputCustomMethod = new JPopupTextField("CUSTOM"){
214 @Override
215 public JToolTip createToolTip() {
216 return tooltipMethods.get();
217 }
218 }.getProxy();
219 inputCustomMethod.addMouseListener(new MouseAdapter() {
220 @Override
221 public void mouseClicked(MouseEvent e) {
222 radioCustomMethod.setSelected(!radioCustomMethod.isSelected());
223 }
224 });
225 inputCustomMethod.getDocument().addDocumentListener(new DocumentListenerEditing() {
226 @Override
227 public void process() {
228 PanelAddressBar.this.validate(inputCustomMethod);
229 }
230 });
231 radioCustomMethod.addActionListener(actionEvent -> this.validate(inputCustomMethod));
232
233 var tooltipCustomMethod = "<html>Set user defined HTTP method.<br/>" +
234 "A valid method is limited to chars:<br>" +
235 "!#$%&'*+-.^_`|~0123456789<br>" +
236 "abcdefghijklmnopqrstuvwxyz<br>" +
237 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
238 "</html>";
239 MouseAdapter mouseAdapterSetBackground = new MouseAdapter() {
240 @Override
241 public void mouseEntered(MouseEvent e) {
242 super.mouseEntered(e);
243 panelCustomMethod.setBackground(colorSelectionBackground.get());
244 radioCustomMethod.setBackground(colorSelectionBackground.get());
245 }
246 @Override
247 public void mouseExited(MouseEvent e) {
248 super.mouseExited(e);
249 panelCustomMethod.setBackground(colorBackground.get());
250 radioCustomMethod.setBackground(colorBackground.get());
251 }
252 };
253 Arrays.asList(radioCustomMethod, inputCustomMethod, panelCustomMethod).forEach(component -> {
254 component.addMouseListener(mouseAdapterSetBackground);
255 component.setToolTipText(tooltipCustomMethod);
256 });
257
258 panelCustomMethod.add(radioCustomMethod, BorderLayout.LINE_START);
259 panelCustomMethod.add(inputCustomMethod, BorderLayout.CENTER);
260 popupMethods.insert(panelCustomMethod, popupMethods.getComponentCount());
261
262 this.atomicRadioMethod.get().addMouseListener(new MouseAdapter() {
263 @Override
264 public void mousePressed(MouseEvent e) {
265 Arrays.stream(popupMethods.getComponents()).map(a -> (JComponent) a).forEach(JComponent::updateUI);
266 radioCustomMethod.setIcon(new FlatRadioButtonMenuItemIcon());
267 radioCustomMethod.updateUI();
268 inputCustomMethod.updateUI();
269 popupMethods.updateUI();
270
271 if (ComponentOrientation.RIGHT_TO_LEFT.equals(ComponentOrientation.getOrientation(I18nUtil.getCurrentLocale()))) {
272 radioCustomMethod.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 6));
273 } else {
274 radioCustomMethod.setBorder(BorderFactory.createEmptyBorder(0, 6, 0, 0));
275 }
276
277 popupMethods.show(
278 e.getComponent(),
279 ComponentOrientation.RIGHT_TO_LEFT.equals(ComponentOrientation.getOrientation(I18nUtil.getCurrentLocale()))
280 ? e.getComponent().getX() - e.getComponent().getWidth() - popupMethods.getWidth()
281 : e.getComponent().getX(),
282 e.getComponent().getY() + e.getComponent().getHeight()
283 );
284 popupMethods.setLocation(
285 ComponentOrientation.RIGHT_TO_LEFT.equals(ComponentOrientation.getOrientation(I18nUtil.getCurrentLocale()))
286 ? e.getComponent().getLocationOnScreen().x + e.getComponent().getWidth() - popupMethods.getWidth()
287 : e.getComponent().getLocationOnScreen().x,
288 e.getComponent().getLocationOnScreen().y + e.getComponent().getHeight()
289 );
290
291
292 popupMethods.applyComponentOrientation(ComponentOrientation.getOrientation(I18nUtil.getCurrentLocale()));
293 }
294 });
295
296 groupLayout.setHorizontalGroup(
297 groupLayout
298 .createSequentialGroup()
299 .addGroup(
300 groupLayout
301 .createParallelGroup(GroupLayout.Alignment.TRAILING, false)
302 .addComponent(this.atomicRadioRequest.get())
303 .addComponent(this.atomicRadioMethod.get())
304 .addComponent(this.atomicRadioHeader.get())
305 )
306 .addGroup(
307 groupLayout
308 .createParallelGroup()
309 .addComponent(this.atomicTextFieldAddress.get())
310 .addComponent(this.atomicTextFieldRequest.get())
311 .addComponent(this.atomicTextFieldHeader.get())
312 )
313 .addGroup(
314 groupLayout
315 .createParallelGroup(GroupLayout.Alignment.LEADING, false)
316 .addComponent(advancedButton)
317 )
318 );
319
320 groupLayout.setVerticalGroup(
321 groupLayout
322 .createSequentialGroup()
323 .addGroup(
324 groupLayout
325 .createParallelGroup(GroupLayout.Alignment.CENTER, false)
326 .addComponent(this.atomicRadioRequest.get())
327 .addComponent(this.atomicTextFieldAddress.get())
328 .addComponent(advancedButton)
329 )
330 .addGroup(
331 groupLayout
332 .createParallelGroup(GroupLayout.Alignment.BASELINE)
333 .addComponent(this.atomicRadioMethod.get())
334 .addComponent(this.atomicTextFieldRequest.get())
335 )
336 .addGroup(
337 groupLayout
338 .createParallelGroup(GroupLayout.Alignment.BASELINE)
339 .addComponent(this.atomicRadioHeader.get())
340 .addComponent(this.atomicTextFieldHeader.get())
341 )
342 );
343 }
344
345 private void validate(JTextField inputCustomMethod) {
346 if (StringUtils.isEmpty(inputCustomMethod.getText())) {
347 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Missing custom method label");
348 } else if (ParameterUtil.isInvalidName(inputCustomMethod.getText())) {
349 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, () -> String.format("Illegal method: \"%s\"", inputCustomMethod.getText()));
350 } else {
351 this.typeRequest = inputCustomMethod.getText();
352 this.atomicRadioMethod.get().setText(this.typeRequest);
353 }
354 }
355
356 private JLabel initAdvancedButton() {
357 var tooltip = new AtomicReference<>(new JToolTipI18n(I18nUtil.valueByKey(PanelAddressBar.BUTTON_ADVANCED)));
358 var advancedButton = new JLabel(UiUtil.ARROW_DOWN.getIcon()) {
359 @Override
360 public JToolTip createToolTip() {
361 return tooltip.get();
362 }
363 };
364 advancedButton.setName("advancedButton");
365 advancedButton.setToolTipText(I18nUtil.valueByKey(PanelAddressBar.BUTTON_ADVANCED));
366 I18nViewUtil.addComponentForKey(PanelAddressBar.BUTTON_ADVANCED, tooltip.get());
367 advancedButton.addMouseListener(new MouseAdapter() {
368 @Override
369 public void mouseClicked(MouseEvent e) {
370 boolean isVisible = advancedButton.getIcon() == UiUtil.ARROW_DOWN.getIcon();
371 PanelAddressBar.this.atomicTextFieldRequest.get().setVisible(isVisible);
372 PanelAddressBar.this.atomicTextFieldHeader.get().setVisible(isVisible);
373 PanelAddressBar.this.atomicRadioRequest.get().setVisible(isVisible);
374 PanelAddressBar.this.atomicRadioMethod.get().setVisible(isVisible);
375 PanelAddressBar.this.atomicRadioHeader.get().setVisible(isVisible);
376 PanelAddressBar.this.isAdvanceActivated = isVisible;
377 MediatorHelper.menubar().setVisible(isVisible);
378 advancedButton.setIcon(isVisible ? UiUtil.ARROW_UP.getIcon() : UiUtil.ARROW_DOWN.getIcon());
379 }
380 });
381 return advancedButton;
382 }
383
384
385
386
387 public void setMethodInjection(AbstractMethodInjection methodInjection) {
388 this.methodInjection = methodInjection;
389 }
390
391 public boolean isAdvanceActivated() {
392 return !this.isAdvanceActivated;
393 }
394
395 public JTextField getTextFieldAddress() {
396 return this.atomicTextFieldAddress.get();
397 }
398
399 public JTextField getTextFieldRequest() {
400 return this.atomicTextFieldRequest.get();
401 }
402
403 public JTextField getTextFieldHeader() {
404 return this.atomicTextFieldHeader.get();
405 }
406
407 public AbstractMethodInjection getMethodInjection() {
408 return this.methodInjection;
409 }
410
411 public PanelTrailingAddress getPanelTrailingAddress() {
412 return this.panelTrailingAddress;
413 }
414
415 public String getTypeRequest() {
416 return this.typeRequest;
417 }
418
419 public JRadioButton getAtomicRadioRequest() {
420 return this.atomicRadioRequest.get();
421 }
422
423 public JRadioButton getAtomicRadioMethod() {
424 return this.atomicRadioMethod.get();
425 }
426
427 public JRadioButton getAtomicRadioHeader() {
428 return this.atomicRadioHeader.get();
429 }
430 }