View Javadoc
1   package com.jsql.view.swing.panel.address;
2   
3   import com.jsql.model.injection.strategy.AbstractStrategy;
4   import com.jsql.model.injection.strategy.StrategyError;
5   import com.jsql.model.injection.engine.model.Engine;
6   import com.jsql.model.injection.engine.model.yaml.Method;
7   import com.jsql.util.I18nUtil;
8   import com.jsql.util.LogLevelUtil;
9   import com.jsql.view.swing.panel.PanelAddressBar;
10  import com.jsql.view.swing.text.JToolTipI18n;
11  import com.jsql.view.swing.util.I18nViewUtil;
12  import com.jsql.view.swing.util.MediatorHelper;
13  import com.jsql.view.swing.util.UiUtil;
14  import org.apache.logging.log4j.LogManager;
15  import org.apache.logging.log4j.Logger;
16  
17  import javax.swing.*;
18  import java.awt.*;
19  import java.awt.event.MouseAdapter;
20  import java.awt.event.MouseEvent;
21  import java.util.Arrays;
22  import java.util.Locale;
23  import java.util.concurrent.atomic.AtomicReference;
24  import java.util.stream.StreamSupport;
25  
26  public class PanelTrailingAddress extends JPanel {
27  
28      private static final Logger LOGGER = LogManager.getRootLogger();
29  
30      public static final String MENU_STRATEGY = "menuStrategy";
31      public static final String ITEM_RADIO_STRATEGY_ERROR = "itemRadioStrategyError";
32      public static final String MENU_VENDOR = "menuVendor";
33      public static final String ITEM_RADIO_VENDOR = "itemRadioVendor";
34      public static final String PARAM_AUTO = "Param auto";
35  
36      private JMenu itemRadioStrategyError;
37  
38      private final JLabel labelTarget = new JLabel(UiUtil.ARROW_DOWN.getIcon(), SwingConstants.LEFT);
39      private final JLabel labelEngine = new JLabel(UiUtil.ARROW_DOWN.getIcon(), SwingConstants.LEFT);
40      private final JLabel labelStrategy = new JLabel(UiUtil.ARROW_DOWN.getIcon(), SwingConstants.LEFT);
41      private ButtonGroup groupRadio = new ButtonGroup();
42      private final JPopupMenu popupMenuEngines = new JPopupMenu();
43      private final JPopupMenu popupMenuStrategies = new JPopupMenu();
44  
45      private final ButtonGroup groupStrategy = new ButtonGroup();
46      public static final String PREFIX_NAME_ERROR = "itemRadioError";
47      private static final String I18N_TOOLTIP_STRATEGY = "STRATEGY_%s_TOOLTIP";
48  
49      /**
50       * Loader displayed during injection.
51       */
52      private final JProgressBar loader;
53  
54      /**
55       * Connection button.
56       */
57      public final ButtonStart buttonStart = new ButtonStart();
58      
59      public PanelTrailingAddress(PanelAddressBar panelAddressBar) {
60          this.buttonStart.addActionListener(new ActionStart(panelAddressBar));
61          this.setOpaque(false);
62          this.setBorder(null);
63          this.labelStrategy.setText("Strategy auto");
64          this.labelStrategy.setName(PanelTrailingAddress.MENU_STRATEGY);
65  
66          for (final AbstractStrategy strategy: MediatorHelper.model().getMediatorStrategy().getStrategies()) {
67              var nameStrategy = strategy.getName().toUpperCase(Locale.ROOT);
68              JMenuItem itemRadioStrategy;
69              if (strategy == MediatorHelper.model().getMediatorStrategy().getError()) {
70                  itemRadioStrategy = new JMenu(strategy.toString());
71                  this.itemRadioStrategyError = (JMenu) itemRadioStrategy;
72                  itemRadioStrategy.getComponent().setName(PanelTrailingAddress.ITEM_RADIO_STRATEGY_ERROR);
73              } else {
74                  var atomicTooltip = new AtomicReference<>(new JToolTipI18n(
75                      I18nUtil.valueByKey(String.format(PanelTrailingAddress.I18N_TOOLTIP_STRATEGY, nameStrategy))
76                  ));
77                  itemRadioStrategy = new JRadioButtonMenuItem(strategy.toString()) {
78                      @Override
79                      public JToolTip createToolTip() {
80                          atomicTooltip.set(new JToolTipI18n(
81                              I18nUtil.valueByKey(
82                                  String.format(PanelTrailingAddress.I18N_TOOLTIP_STRATEGY, nameStrategy)
83                              )
84                          ));
85                          return atomicTooltip.get();
86                      }
87                  };
88                  I18nViewUtil.addComponentForKey(
89                      String.format(PanelTrailingAddress.I18N_TOOLTIP_STRATEGY, nameStrategy),
90                      atomicTooltip.get()
91                  );
92                  itemRadioStrategy.getComponent().setName("itemRadioStrategy" + strategy);
93                  itemRadioStrategy.addActionListener(actionEvent -> {
94                      this.labelStrategy.setText(strategy.toString());
95                      MediatorHelper.model().getMediatorStrategy().setStrategy(strategy);
96                  });
97                  this.groupStrategy.add(itemRadioStrategy);
98              }
99  
100             this.popupMenuStrategies.add(itemRadioStrategy);
101             itemRadioStrategy.setToolTipText(
102                 I18nUtil.valueByKey(String.format(PanelTrailingAddress.I18N_TOOLTIP_STRATEGY, nameStrategy))
103             );
104             itemRadioStrategy.setEnabled(false);
105         }
106 
107         this.labelEngine.setText(MediatorHelper.model().getMediatorEngine().getAuto().toString());
108         this.labelEngine.setName(PanelTrailingAddress.MENU_VENDOR);
109         this.popupMenuEngines.setLayout(UiUtil.getColumnLayout(MediatorHelper.model().getMediatorEngine().getEngines().size()));
110         var groupEngine = new ButtonGroup();
111         for (final Engine engine : MediatorHelper.model().getMediatorEngine().getEngines()) {
112             JMenuItem itemRadioEngine = new JRadioButtonMenuItem(engine.toString(), engine == MediatorHelper.model().getMediatorEngine().getAuto());
113             itemRadioEngine.setName(PanelTrailingAddress.ITEM_RADIO_VENDOR + engine);
114             itemRadioEngine.addActionListener(actionEvent -> {
115                 this.labelEngine.setText(engine.toString());
116                 MediatorHelper.model().getMediatorEngine().setEngineByUser(engine);
117             });
118             this.popupMenuEngines.add(itemRadioEngine);
119             groupEngine.add(itemRadioEngine);
120         }
121 
122         this.loader = new JProgressBar();
123         var dimension = UIManager.getDimension("ProgressBar.horizontalSize");
124         this.loader.setPreferredSize(new Dimension(32, dimension.height));
125         this.loader.setIndeterminate(true);
126         this.add(this.loader);
127 
128         this.labelEngine.addMouseListener(new MouseAdapter() {
129             @Override
130             public void mousePressed(MouseEvent e) {
131                 Arrays.stream(PanelTrailingAddress.this.popupMenuEngines.getComponents())
132                     .map(JComponent.class::cast)
133                     .forEach(JComponent::updateUI);  // required: incorrect when dark/light mode switch
134                 PanelTrailingAddress.this.popupMenuEngines.updateUI();  // required: incorrect when dark/light mode switch
135                 SwingUtilities.invokeLater(() -> {  // reduce flickering on linux
136                     PanelTrailingAddress.this.popupMenuEngines.show(e.getComponent(), e.getComponent().getX(),5 + e.getComponent().getY() + e.getComponent().getHeight());
137                     PanelTrailingAddress.this.popupMenuEngines.setLocation(e.getComponent().getLocationOnScreen().x,5 + e.getComponent().getLocationOnScreen().y + e.getComponent().getHeight());
138                 });
139             }
140         });
141         this.labelStrategy.addMouseListener(new MouseAdapter() {
142             @Override
143             public void mousePressed(MouseEvent e) {
144                 Arrays.stream(PanelTrailingAddress.this.popupMenuStrategies.getComponents())
145                     .map(JComponent.class::cast)
146                     .forEach(JComponent::updateUI);  // required: incorrect when dark/light mode switch
147                 for (var i = 0 ; i < PanelTrailingAddress.this.getMenuError().getItemCount() ; i++) {
148                     PanelTrailingAddress.this.getMenuError().getItem(i).updateUI();  // required: incorrect when dark/light mode switch
149                 }
150                 PanelTrailingAddress.this.popupMenuStrategies.updateUI();  // required: incorrect when dark/light mode switch
151                 SwingUtilities.invokeLater(() -> {  // reduce flickering on linux
152                     PanelTrailingAddress.this.popupMenuStrategies.show(e.getComponent(), e.getComponent().getX(),5 + e.getComponent().getY() + e.getComponent().getHeight());
153                     PanelTrailingAddress.this.popupMenuStrategies.setLocation(e.getComponent().getLocationOnScreen().x,5 + e.getComponent().getLocationOnScreen().y + e.getComponent().getHeight());
154                 });
155             }
156         });
157 
158         this.labelTarget.setText(PanelTrailingAddress.PARAM_AUTO);
159         this.labelTarget.addMouseListener(new TargetMouseAdapter(this, panelAddressBar));
160 
161         this.add(this.labelTarget);
162         this.add(this.labelEngine);
163         this.add(this.labelStrategy);
164         this.add(this.buttonStart);
165         this.setCursor(Cursor.getDefaultCursor());
166         this.loader.setVisible(false);
167     }
168 
169     public void endPreparation() {
170         this.buttonStart.setToolTipText(I18nUtil.valueByKey("BUTTON_START_TOOLTIP"));
171         this.buttonStart.setInjectionReady();
172         this.loader.setVisible(false);
173     }
174     
175     public void reset() {
176         this.labelStrategy.setText("Strategy auto");
177         if (MediatorHelper.model().getMediatorEngine().getEngineByUser() == MediatorHelper.model().getMediatorEngine().getAuto()) {
178             this.labelEngine.setText(MediatorHelper.model().getMediatorEngine().getAuto().toString());
179         }
180         Arrays.stream(this.popupMenuStrategies.getComponents())
181             .forEach(component -> component.setEnabled(false));
182         this.getMenuError().removeAll();
183 
184         this.groupStrategy.clearSelection();
185         Iterable<AbstractButton> iterable = () -> this.groupStrategy.getElements().asIterator();
186         StreamSupport.stream(iterable.spliterator(), false)
187             .filter(abstractButton -> abstractButton.getName().startsWith(PanelTrailingAddress.PREFIX_NAME_ERROR))
188             .forEach(this.groupStrategy::remove);
189     }
190     
191     public void setEngine(Engine engine) {
192         this.labelEngine.setText(engine.toString());
193         this.itemRadioStrategyError.removeAll();
194         var indexError = 0;
195         if (
196             engine != MediatorHelper.model().getMediatorEngine().getAuto()
197             && engine.instance().getModelYaml().getStrategy().getError() != null
198         ) {
199             for (Method methodError: engine.instance().getModelYaml().getStrategy().getError().getMethod()) {
200                 JMenuItem itemRadioError = new JRadioButtonMenuItem(methodError.getName());
201                 itemRadioError.setEnabled(false);
202                 itemRadioError.setName(PanelTrailingAddress.PREFIX_NAME_ERROR + methodError.getName());
203                 this.itemRadioStrategyError.add(itemRadioError);
204                 this.groupStrategy.add(itemRadioError);
205                 int indexErrorFinal = indexError;
206                 itemRadioError.addActionListener(actionEvent -> {
207                     this.labelStrategy.setText(methodError.getName());
208                     MediatorHelper.model().getMediatorStrategy().setStrategy(MediatorHelper.model().getMediatorStrategy().getError());
209                     MediatorHelper.model().getMediatorStrategy().getError().setIndexErrorStrategy(indexErrorFinal);
210                 });
211                 indexError++;
212             }
213         }
214     }
215     
216     public void activateStrategy(AbstractStrategy strategy) {
217         if (strategy instanceof StrategyError strategyError) {
218             this.labelStrategy.setText(strategyError.toString());
219             int indexError = strategyError.getIndexErrorStrategy();
220             String nameError = MediatorHelper.model().getMediatorEngine().getEngine().instance().getModelYaml().getStrategy().getError().getMethod().get(
221                 indexError
222             ).getName();
223 
224             Arrays.stream(this.getMenuError().getMenuComponents())
225                 .map(JRadioButtonMenuItem.class::cast)
226                 .filter(component -> component.getText().equals(nameError))
227                 .forEach(jRadioButtonMenuItem -> {
228                     jRadioButtonMenuItem.setSelected(true);
229                     this.labelStrategy.setText(nameError);
230                 });
231         } else {
232             this.labelStrategy.setText(strategy.toString());
233             Arrays.stream(this.popupMenuStrategies.getComponents())
234                 .map(JMenuItem.class::cast)
235                 .filter(jMenuItem -> jMenuItem.getText().equals(strategy.toString()))
236                 .forEach(jMenuItem -> jMenuItem.setSelected(true));
237         }
238     }
239 
240     public void markInvulnerable(AbstractStrategy strategy) {
241         Arrays.stream(this.popupMenuStrategies.getComponents())
242             .map(JMenuItem.class::cast)
243             .filter(jMenuItem -> jMenuItem.getText().equals(strategy.toString()))
244             .forEach(jMenuItem -> jMenuItem.setEnabled(false));
245     }
246     
247     public void markInvulnerable(int indexMethodError, AbstractStrategy strategy) {
248         Arrays.stream(this.popupMenuStrategies.getSubElements())
249             .map(JMenuItem.class::cast)
250             .filter(jMenuItem -> jMenuItem.getText().equals(strategy.toString()))
251             .map(JMenu.class::cast)
252             .filter(jMenuItem -> {
253                 var isNotNull = true;
254                 // Fix #36975: ArrayIndexOutOfBoundsException on getItem()
255                 // Fix #40352: NullPointerException on ?
256                 // Fix #95855: NPE on setEnabled()
257                 try {
258                     isNotNull = jMenuItem.getItem(indexMethodError) != null;
259                 } catch (ArrayIndexOutOfBoundsException e) {
260                     LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
261                     return false;
262                 }
263                 return isNotNull;
264             })
265             .forEach(jMenuItem -> jMenuItem.getItem(indexMethodError).setEnabled(false));
266     }
267 
268     private JMenu getMenuError() {
269         var nameError = MediatorHelper.model().getMediatorStrategy().getError().getName();
270         return (JMenu) Arrays.stream(this.popupMenuStrategies.getComponents())
271             .map(JMenuItem.class::cast)
272             .filter(jMenuItem -> jMenuItem.getText().equalsIgnoreCase(nameError))
273             .findFirst()
274             .orElse(new JMenuItem("Mock"));
275     }
276 
277     public void markVulnerable(AbstractStrategy strategy) {
278         Arrays.stream(this.popupMenuStrategies.getComponents())
279             .map(JMenuItem.class::cast)
280             .filter(jMenuItem -> jMenuItem.getText().equals(strategy.toString()))
281             .forEach(jMenuItem -> jMenuItem.setEnabled(true));
282     }
283 
284     public void markVulnerable(int indexMethodError, AbstractStrategy strategy) {
285         // Fix #46578: ArrayIndexOutOfBoundsException on getItem()
286         try {
287             Arrays.stream(this.popupMenuStrategies.getComponents())
288                 .map(JMenuItem.class::cast)
289                 .filter(jMenuItem -> jMenuItem.getText().equals(strategy.toString()))
290                 .map(JMenu.class::cast)
291                 .filter(jMenuItem -> jMenuItem.getItem(indexMethodError) != null)
292                 .forEach(jMenuItem -> {
293                     jMenuItem.setEnabled(true);
294                     jMenuItem.getItem(indexMethodError).setEnabled(true);
295                 });
296         } catch (ArrayIndexOutOfBoundsException e) {
297             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
298         }
299     }
300 
301 
302     // Getter and setter
303 
304     public JComponent getLoader() {
305         return this.loader;
306     }
307 
308     public ButtonStart getButtonStart() {
309         return this.buttonStart;
310     }
311 
312     public JLabel getLabelTarget() {
313         return this.labelTarget;
314     }
315 
316     public ButtonGroup getGroupRadio() {
317         return this.groupRadio;
318     }
319 
320     public void setGroupRadio(ButtonGroup groupRadio) {
321         this.groupRadio = groupRadio;
322     }
323 }