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