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