| 1 | /******************************************************************************* | |
| 2 | * Copyhacked (H) 2012-2025. | |
| 3 | * This program and the accompanying materials | |
| 4 | * are made available under no term at all, use it like | |
| 5 | * you want, but share and discuss it | |
| 6 | * every time possible with every body. | |
| 7 | * | |
| 8 | * Contributors: | |
| 9 | * ron190 at ymail dot com - initial implementation | |
| 10 | ******************************************************************************/ | |
| 11 | package com.jsql.view.swing.interaction; | |
| 12 | ||
| 13 | import com.jsql.util.I18nUtil; | |
| 14 | import com.jsql.util.LogLevelUtil; | |
| 15 | import com.jsql.view.interaction.InteractionCommand; | |
| 16 | import com.jsql.view.swing.tab.TabHeader; | |
| 17 | import com.jsql.view.swing.util.I18nViewUtil; | |
| 18 | import com.jsql.view.swing.util.MediatorHelper; | |
| 19 | import com.jsql.view.swing.util.UiUtil; | |
| 20 | import org.apache.commons.lang3.StringUtils; | |
| 21 | import org.apache.logging.log4j.LogManager; | |
| 22 | import org.apache.logging.log4j.Logger; | |
| 23 | import org.jsoup.Jsoup; | |
| 24 | import org.jsoup.safety.Safelist; | |
| 25 | ||
| 26 | import javax.swing.*; | |
| 27 | import javax.swing.text.DefaultEditorKit; | |
| 28 | import java.awt.*; | |
| 29 | import java.awt.datatransfer.StringSelection; | |
| 30 | import java.awt.event.*; | |
| 31 | import java.io.IOException; | |
| 32 | import java.util.EmptyStackException; | |
| 33 | ||
| 34 | /** | |
| 35 | * Create a new tab for an administration webpage. | |
| 36 | */ | |
| 37 | public class CreateAdminPageTab extends CreateTabHelper implements InteractionCommand { | |
| 38 | | |
| 39 | private static final Logger LOGGER = LogManager.getRootLogger(); | |
| 40 | ||
| 41 | /** | |
| 42 | * Url for the administration webpage. | |
| 43 | */ | |
| 44 | private final String url; | |
| 45 | ||
| 46 | /** | |
| 47 | * @param interactionParams Url of the webpage | |
| 48 | */ | |
| 49 | public CreateAdminPageTab(Object[] interactionParams) { | |
| 50 | this.url = (String) interactionParams[0]; | |
| 51 | } | |
| 52 | ||
| 53 | @Override | |
| 54 | public void execute() { | |
| 55 | String htmlSource = StringUtils.EMPTY; | |
| 56 | | |
| 57 | // Fix #4081: SocketTimeoutException on get() | |
| 58 | // Fix #44642: NoClassDefFoundError on get() | |
| 59 | // Fix #44641: ExceptionInInitializerError on get() | |
| 60 | try { | |
| 61 | // Previous test for 2xx Success and 3xx Redirection was Header only, | |
| 62 | // now get the HTML content. | |
| 63 | // Proxy is used by jsoup | |
| 64 | htmlSource = Jsoup.clean( | |
| 65 | Jsoup.connect(this.url) | |
| 66 | // Prevent exception on UnsupportedMimeTypeException: Unhandled content type. Must be text/*, application/xml, or application/*+xml | |
| 67 | .ignoreContentType(true) | |
| 68 | // Prevent exception on HTTP errors | |
| 69 | .ignoreHttpErrors(true) | |
| 70 | .get() | |
| 71 | .html() | |
| 72 | .replaceAll("<img[^>]*>", StringUtils.EMPTY) | |
| 73 | .replaceAll("<input[^>]*type=\"?hidden\"?[^>]*>", StringUtils.EMPTY) | |
| 74 | .replaceAll("<input[^>]*type=\"?(submit|button)\"?[^>]*>", "<div style=\"background-color:#eeeeee;text-align:center;border:1px solid black;width:100px;\">button</div>") | |
| 75 | .replaceAll("<input[^>]*>", "<div style=\"text-align:center;border:1px solid black;width:100px;\">input</div>"), | |
| 76 | Safelist.relaxed() | |
| 77 | .addTags("center", "div", "span") | |
| 78 | .addAttributes(":all", "style") | |
| 79 | ); | |
| 80 | } catch (IOException e) { | |
| 81 | LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Failure opening page: {}", e.getMessage()); | |
| 82 | } catch (ExceptionInInitializerError | NoClassDefFoundError e) { | |
| 83 | LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e); | |
| 84 | } | |
| 85 | ||
| 86 | final var browser = new JTextPane(); | |
| 87 |
1
1. execute : removed call to javax/swing/JTextPane::setContentType → NO_COVERAGE |
browser.setContentType("text/html"); |
| 88 |
1
1. execute : removed call to javax/swing/JTextPane::setEditable → NO_COVERAGE |
browser.setEditable(false); |
| 89 |
1
1. execute : removed call to javax/swing/JTextPane::setCaretPosition → NO_COVERAGE |
browser.setCaretPosition(0); |
| 90 | ||
| 91 | // Fix #43220: EmptyStackException on setText() | |
| 92 | // Fix #94242: IndexOutOfBoundsException on setText() | |
| 93 | try { | |
| 94 |
1
1. execute : removed call to javax/swing/JTextPane::setText → NO_COVERAGE |
browser.setText(htmlSource); |
| 95 | } catch (IndexOutOfBoundsException | EmptyStackException e) { | |
| 96 | LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e); | |
| 97 | } | |
| 98 | ||
| 99 | JMenuItem itemCopyUrl = new JMenuItem(I18nUtil.valueByKey("CONTEXT_MENU_COPY_PAGE_URL")); | |
| 100 |
1
1. execute : removed call to com/jsql/view/swing/util/I18nViewUtil::addComponentForKey → NO_COVERAGE |
I18nViewUtil.addComponentForKey("CONTEXT_MENU_COPY_PAGE_URL", itemCopyUrl); |
| 101 | ||
| 102 | JMenuItem itemCopy = new JMenuItem(); | |
| 103 |
1
1. execute : removed call to javax/swing/JMenuItem::setAction → NO_COVERAGE |
itemCopy.setAction(browser.getActionMap().get(DefaultEditorKit.copyAction)); |
| 104 |
1
1. execute : removed call to javax/swing/JMenuItem::setAccelerator → NO_COVERAGE |
itemCopy.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_DOWN_MASK)); |
| 105 |
1
1. execute : removed call to javax/swing/JMenuItem::setMnemonic → NO_COVERAGE |
itemCopy.setMnemonic('C'); |
| 106 |
1
1. execute : removed call to javax/swing/JMenuItem::setText → NO_COVERAGE |
itemCopy.setText(I18nUtil.valueByKey("CONTEXT_MENU_COPY")); |
| 107 |
1
1. execute : removed call to com/jsql/view/swing/util/I18nViewUtil::addComponentForKey → NO_COVERAGE |
I18nViewUtil.addComponentForKey("CONTEXT_MENU_COPY", itemCopy); |
| 108 | ||
| 109 | JMenuItem itemSelectAll = new JMenuItem(); | |
| 110 |
1
1. execute : removed call to javax/swing/JMenuItem::setAction → NO_COVERAGE |
itemSelectAll.setAction(browser.getActionMap().get(DefaultEditorKit.selectAllAction)); |
| 111 |
1
1. execute : removed call to javax/swing/JMenuItem::setAccelerator → NO_COVERAGE |
itemSelectAll.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_DOWN_MASK)); |
| 112 |
1
1. execute : removed call to javax/swing/JMenuItem::setText → NO_COVERAGE |
itemSelectAll.setText(I18nUtil.valueByKey("CONTEXT_MENU_SELECT_ALL")); |
| 113 |
1
1. execute : removed call to com/jsql/view/swing/util/I18nViewUtil::addComponentForKey → NO_COVERAGE |
I18nViewUtil.addComponentForKey("CONTEXT_MENU_SELECT_ALL", itemSelectAll); |
| 114 |
1
1. execute : removed call to javax/swing/JMenuItem::setMnemonic → NO_COVERAGE |
itemSelectAll.setMnemonic('A'); |
| 115 | ||
| 116 | final var menu = new JPopupMenu(); | |
| 117 | menu.add(itemCopyUrl); | |
| 118 | menu.add(new JSeparator()); | |
| 119 | menu.add(itemCopy); | |
| 120 | menu.add(itemSelectAll); | |
| 121 |
1
1. execute : removed call to javax/swing/JPopupMenu::applyComponentOrientation → NO_COVERAGE |
menu.applyComponentOrientation(ComponentOrientation.getOrientation(I18nUtil.getCurrentLocale())); |
| 122 | ||
| 123 |
1
1. execute : removed call to javax/swing/JMenuItem::addActionListener → NO_COVERAGE |
itemCopyUrl.addActionListener(actionEvent -> { |
| 124 | var stringSelection = new StringSelection(this.url); | |
| 125 | var clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); | |
| 126 |
1
1. lambda$execute$0 : removed call to java/awt/datatransfer/Clipboard::setContents → NO_COVERAGE |
clipboard.setContents(stringSelection, null); |
| 127 | }); | |
| 128 | ||
| 129 |
2
1. lambda$execute$1 : removed call to javax/swing/JTextPane::selectAll → NO_COVERAGE 2. execute : removed call to javax/swing/JMenuItem::addActionListener → NO_COVERAGE |
itemSelectAll.addActionListener(actionEvent -> browser.selectAll()); |
| 130 | | |
| 131 |
1
1. execute : removed call to javax/swing/JTextPane::addFocusListener → NO_COVERAGE |
browser.addFocusListener(new FocusAdapter() { |
| 132 | @Override | |
| 133 | public void focusGained(FocusEvent focusEvent) { | |
| 134 |
1
1. focusGained : removed call to javax/swing/text/Caret::setVisible → NO_COVERAGE |
browser.getCaret().setVisible(true); |
| 135 |
1
1. focusGained : removed call to javax/swing/text/Caret::setSelectionVisible → NO_COVERAGE |
browser.getCaret().setSelectionVisible(true); |
| 136 | } | |
| 137 | }); | |
| 138 | | |
| 139 |
1
1. execute : removed call to javax/swing/JTextPane::addMouseListener → NO_COVERAGE |
browser.addMouseListener(new MouseAdapter() { |
| 140 | @Override | |
| 141 | public void mousePressed(MouseEvent evt) { | |
| 142 | browser.requestFocusInWindow(); | |
| 143 |
1
1. mousePressed : negated conditional → NO_COVERAGE |
if (evt.isPopupTrigger()) { |
| 144 |
1
1. mousePressed : removed call to javax/swing/JPopupMenu::show → NO_COVERAGE |
menu.show(evt.getComponent(), evt.getX(), evt.getY()); |
| 145 | } | |
| 146 | } | |
| 147 | @Override | |
| 148 | public void mouseReleased(MouseEvent evt) { | |
| 149 |
1
1. mouseReleased : negated conditional → NO_COVERAGE |
if (evt.isPopupTrigger()) { |
| 150 | // Fix #45348: IllegalComponentStateException on show() | |
| 151 | try { | |
| 152 |
1
1. mouseReleased : removed call to javax/swing/JPopupMenu::show → NO_COVERAGE |
menu.show(evt.getComponent(), evt.getX(), evt.getY()); |
| 153 | } catch (IllegalComponentStateException e) { | |
| 154 | LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e); | |
| 155 | } | |
| 156 |
1
1. mouseReleased : removed call to javax/swing/JPopupMenu::setLocation → NO_COVERAGE |
menu.setLocation( |
| 157 |
1
1. mouseReleased : negated conditional → NO_COVERAGE |
ComponentOrientation.RIGHT_TO_LEFT.equals(ComponentOrientation.getOrientation(I18nUtil.getCurrentLocale())) |
| 158 |
1
1. mouseReleased : Replaced integer subtraction with addition → NO_COVERAGE |
? evt.getXOnScreen() - menu.getWidth() |
| 159 | : evt.getXOnScreen(), | |
| 160 | evt.getYOnScreen() | |
| 161 | ); | |
| 162 | } | |
| 163 | } | |
| 164 | }); | |
| 165 | ||
| 166 | final var scroller = new JScrollPane(browser); | |
| 167 |
1
1. execute : removed call to com/jsql/view/swing/tab/TabResults::addTab → NO_COVERAGE |
MediatorHelper.tabResults().addTab(this.url.replaceAll(".*/", StringUtils.EMPTY) + StringUtils.SPACE, scroller); |
| 168 | try { // Fix #96175: ArrayIndexOutOfBoundsException on setSelectedComponent() | |
| 169 |
1
1. execute : removed call to com/jsql/view/swing/tab/TabResults::setSelectedComponent → NO_COVERAGE |
MediatorHelper.tabResults().setSelectedComponent(scroller); // Focus on the new tab |
| 170 | } catch (ArrayIndexOutOfBoundsException e) { | |
| 171 | LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e); | |
| 172 | } | |
| 173 |
1
1. execute : removed call to com/jsql/view/swing/tab/TabResults::setToolTipTextAt → NO_COVERAGE |
MediatorHelper.tabResults().setToolTipTextAt( |
| 174 | MediatorHelper.tabResults().indexOfComponent(scroller), | |
| 175 | String.format("<html>%s</html>", this.url) | |
| 176 | ); | |
| 177 | ||
| 178 | // Create a custom tab header | |
| 179 | var header = new TabHeader( | |
| 180 | this.url.replaceAll(".*/", StringUtils.EMPTY), | |
| 181 | UiUtil.ADMIN.getIcon() | |
| 182 | ); | |
| 183 |
1
1. execute : removed call to com/jsql/view/swing/tab/TabResults::setTabComponentAt → NO_COVERAGE |
MediatorHelper.tabResults().setTabComponentAt(MediatorHelper.tabResults().indexOfComponent(scroller), header); // Apply the custom header to the tab |
| 184 |
1
1. execute : removed call to javax/swing/JTextPane::setCaretPosition → NO_COVERAGE |
browser.setCaretPosition(0); |
| 185 | ||
| 186 |
1
1. execute : removed call to com/jsql/view/swing/tab/TabResults::updateUI → NO_COVERAGE |
MediatorHelper.tabResults().updateUI(); // required: light, open/close prefs, dark => light artifacts |
| 187 | } | |
| 188 | } | |
Mutations | ||
| 87 |
1.1 |
|
| 88 |
1.1 |
|
| 89 |
1.1 |
|
| 94 |
1.1 |
|
| 100 |
1.1 |
|
| 103 |
1.1 |
|
| 104 |
1.1 |
|
| 105 |
1.1 |
|
| 106 |
1.1 |
|
| 107 |
1.1 |
|
| 110 |
1.1 |
|
| 111 |
1.1 |
|
| 112 |
1.1 |
|
| 113 |
1.1 |
|
| 114 |
1.1 |
|
| 121 |
1.1 |
|
| 123 |
1.1 |
|
| 126 |
1.1 |
|
| 129 |
1.1 2.2 |
|
| 131 |
1.1 |
|
| 134 |
1.1 |
|
| 135 |
1.1 |
|
| 139 |
1.1 |
|
| 143 |
1.1 |
|
| 144 |
1.1 |
|
| 149 |
1.1 |
|
| 152 |
1.1 |
|
| 156 |
1.1 |
|
| 157 |
1.1 |
|
| 158 |
1.1 |
|
| 167 |
1.1 |
|
| 169 |
1.1 |
|
| 173 |
1.1 |
|
| 183 |
1.1 |
|
| 184 |
1.1 |
|
| 186 |
1.1 |