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