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.shell; | |
12 | ||
13 | import com.jsql.util.LogLevelUtil; | |
14 | import com.jsql.view.swing.scrollpane.LightScrollPane; | |
15 | import com.jsql.view.swing.util.UiUtil; | |
16 | import org.apache.commons.lang3.StringUtils; | |
17 | import org.apache.logging.log4j.LogManager; | |
18 | import org.apache.logging.log4j.Logger; | |
19 | ||
20 | import javax.swing.*; | |
21 | import javax.swing.text.BadLocationException; | |
22 | import javax.swing.text.Style; | |
23 | import javax.swing.text.StyleConstants; | |
24 | import javax.swing.text.StyledDocument; | |
25 | import java.awt.*; | |
26 | import java.awt.event.MouseMotionListener; | |
27 | import java.net.MalformedURLException; | |
28 | import java.net.URI; | |
29 | import java.net.URISyntaxException; | |
30 | import java.util.UUID; | |
31 | ||
32 | /** | |
33 | * A Terminal completely built from swing text pane. | |
34 | */ | |
35 | public abstract class AbstractShell extends JTextPane { | |
36 | | |
37 | /** | |
38 | * Log4j logger sent to view. | |
39 | */ | |
40 | private static final Logger LOGGER = LogManager.getRootLogger(); | |
41 | ||
42 | /** | |
43 | * True if terminal is processing command. | |
44 | */ | |
45 | private final boolean[] isEdited = {false}; | |
46 | ||
47 | /** | |
48 | * Server name or IP to display on prompt. | |
49 | */ | |
50 | private final String host; | |
51 | ||
52 | /** | |
53 | * User and password for database. | |
54 | */ | |
55 | protected String[] loginPassword = null; | |
56 | ||
57 | private final UUID uuidShell; | |
58 | | |
59 | private final String urlShell; | |
60 | ||
61 | /** | |
62 | * Document used to append colored text. | |
63 | */ | |
64 | private final transient StyledDocument styledDocument = this.getStyledDocument(); | |
65 | ||
66 | /** | |
67 | * Style used for coloring text. | |
68 | */ | |
69 | private final transient Style style = this.addStyle("Necrophagist's next album is 2014.", null); | |
70 | ||
71 | /** | |
72 | * Length of prompt. | |
73 | */ | |
74 | private String prompt = StringUtils.EMPTY; | |
75 | ||
76 | /** | |
77 | * Text to display next caret. | |
78 | */ | |
79 | private final String labelShell; | |
80 | | |
81 | /** | |
82 | * Build a shell instance. | |
83 | * @param uuidShell Unique identifier to discriminate beyond multiple opened terminals | |
84 | * @param urlShell URL of current shell | |
85 | * @param labelShell Type of shell to display on prompt | |
86 | * @throws MalformedURLException | |
87 | */ | |
88 | protected AbstractShell(UUID uuidShell, String urlShell, String labelShell) throws MalformedURLException, URISyntaxException { | |
89 | | |
90 | this.uuidShell = uuidShell; | |
91 | this.urlShell = urlShell; | |
92 | this.labelShell = labelShell; | |
93 | ||
94 | var url = new URI(urlShell).toURL(); | |
95 | this.host = url.getHost(); | |
96 | ||
97 |
1
1. <init> : removed call to com/jsql/view/swing/shell/AbstractShell::setFont → NO_COVERAGE |
this.setFont(new Font(UiUtil.FONT_NAME_MONO_NON_ASIAN, Font.PLAIN, ((Font) UIManager.get("TextPane.font")).getSize())); |
98 |
1
1. <init> : removed call to com/jsql/view/swing/shell/AbstractShell::setCaret → NO_COVERAGE |
this.setCaret(new BlockCaret()); |
99 |
1
1. <init> : removed call to com/jsql/view/swing/shell/AbstractShell::setBackground → NO_COVERAGE |
this.setBackground(Color.BLACK); |
100 |
1
1. <init> : removed call to com/jsql/view/swing/shell/AbstractShell::setForeground → NO_COVERAGE |
this.setForeground(Color.LIGHT_GRAY); |
101 |
1
1. <init> : removed call to com/jsql/view/swing/shell/AbstractShell::setBorder → NO_COVERAGE |
this.setBorder(BorderFactory.createEmptyBorder(0, 0, LightScrollPane.THUMB_SIZE, 0)); |
102 | ||
103 |
1
1. <init> : removed call to com/jsql/view/swing/shell/AbstractShell::displayPrompt → NO_COVERAGE |
this.displayPrompt(true); |
104 | ||
105 |
1
1. <init> : removed call to com/jsql/view/swing/shell/AbstractShell::setCursor → NO_COVERAGE |
this.setCursor(null); |
106 |
1
1. <init> : removed call to com/jsql/view/swing/shell/AbstractShell::setTransferHandler → NO_COVERAGE |
this.setTransferHandler(null); |
107 |
1
1. <init> : removed call to com/jsql/view/swing/shell/AbstractShell::setHighlighter → NO_COVERAGE |
this.setHighlighter(null); |
108 | ||
109 |
1
1. <init> : removed call to com/jsql/view/swing/shell/AbstractShell::addMouseListener → NO_COVERAGE |
this.addMouseListener(new EmptyFocus(this)); |
110 |
1
1. <init> : removed call to com/jsql/view/swing/shell/AbstractShell::addKeyListener → NO_COVERAGE |
this.addKeyListener(new KeyAdapterTerminal(this)); |
111 | } | |
112 | ||
113 | /** | |
114 | * Run when cmd is validated. | |
115 | * @param cmd Command to execute | |
116 | * @param terminalID Unique ID for terminal instance | |
117 | * @param wbhPath URL of shell | |
118 | * @param arg Additional parameters (User and password for SQLShell) | |
119 | */ | |
120 | public abstract void action(String cmd, UUID terminalID, String wbhPath, String... arg); | |
121 | | |
122 | /** | |
123 | * Update terminal and use default behavior. | |
124 | */ | |
125 | public void reset() { | |
126 | | |
127 | this.isEdited[0] = false; | |
128 |
1
1. reset : removed call to com/jsql/view/swing/shell/AbstractShell::setEditable → NO_COVERAGE |
this.setEditable(true); |
129 |
1
1. reset : removed call to com/jsql/view/swing/shell/AbstractShell::displayPrompt → NO_COVERAGE |
this.displayPrompt(false); |
130 |
1
1. reset : removed call to com/jsql/view/swing/shell/AbstractShell::setCaretPosition → NO_COVERAGE |
this.setCaretPosition(this.getDocument().getLength()); |
131 |
1
1. reset : removed call to com/jsql/view/swing/shell/AbstractShell::setCursor → NO_COVERAGE |
this.setCursor(null); |
132 | } | |
133 | ||
134 | /** | |
135 | * Add a text at the end of textpane. | |
136 | * @param string Text to add | |
137 | */ | |
138 | public void append(String string) { | |
139 | try { | |
140 | var doc = this.getDocument(); | |
141 |
1
1. append : removed call to javax/swing/text/Document::insertString → NO_COVERAGE |
doc.insertString(doc.getLength(), string, null); |
142 | | |
143 | } catch (BadLocationException e) { | |
144 | LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e); | |
145 | } | |
146 | } | |
147 | | |
148 | /** | |
149 | * Append prompt to textpane, measure prompt the first time is used. | |
150 | * @param isAddingPrompt Should we measure prompt length? | |
151 | */ | |
152 | public void displayPrompt(boolean isAddingPrompt) { | |
153 | | |
154 |
1
1. displayPrompt : removed call to javax/swing/text/StyleConstants::setUnderline → NO_COVERAGE |
StyleConstants.setUnderline(this.style, true); |
155 |
1
1. displayPrompt : removed call to com/jsql/view/swing/shell/AbstractShell::appendPrompt → NO_COVERAGE |
this.appendPrompt("jsql", Color.LIGHT_GRAY, isAddingPrompt); |
156 |
1
1. displayPrompt : removed call to javax/swing/text/StyleConstants::setUnderline → NO_COVERAGE |
StyleConstants.setUnderline(this.style, false); |
157 | ||
158 |
1
1. displayPrompt : removed call to com/jsql/view/swing/shell/AbstractShell::appendPrompt → NO_COVERAGE |
this.appendPrompt(StringUtils.SPACE + this.labelShell, Color.LIGHT_GRAY, isAddingPrompt); |
159 |
1
1. displayPrompt : removed call to com/jsql/view/swing/shell/AbstractShell::appendPrompt → NO_COVERAGE |
this.appendPrompt("[", new Color(50, 191, 50), isAddingPrompt); |
160 |
1
1. displayPrompt : removed call to com/jsql/view/swing/shell/AbstractShell::appendPrompt → NO_COVERAGE |
this.appendPrompt(this.host, new Color(191, 191, 25), isAddingPrompt); |
161 |
1
1. displayPrompt : removed call to com/jsql/view/swing/shell/AbstractShell::appendPrompt → NO_COVERAGE |
this.appendPrompt("]", new Color(50, 191, 50), isAddingPrompt); |
162 |
1
1. displayPrompt : removed call to com/jsql/view/swing/shell/AbstractShell::appendPrompt → NO_COVERAGE |
this.appendPrompt(" >", new Color(191, 100, 100), isAddingPrompt); |
163 |
1
1. displayPrompt : removed call to com/jsql/view/swing/shell/AbstractShell::appendPrompt → NO_COVERAGE |
this.appendPrompt(StringUtils.SPACE, Color.LIGHT_GRAY, isAddingPrompt); |
164 | } | |
165 | ||
166 | /** | |
167 | * Add a colored string to the textpane, measure prompt at the same time. | |
168 | * @param string Text to append | |
169 | * @param color Color of text | |
170 | * @param isAddingPrompt Should we measure prompt length? | |
171 | */ | |
172 | private void appendPrompt(String string, Color color, boolean isAddingPrompt) { | |
173 | try { | |
174 |
1
1. appendPrompt : removed call to javax/swing/text/StyleConstants::setForeground → NO_COVERAGE |
StyleConstants.setForeground(this.style, color); |
175 |
1
1. appendPrompt : removed call to javax/swing/text/StyledDocument::insertString → NO_COVERAGE |
this.styledDocument.insertString(this.styledDocument.getLength(), string, this.style); |
176 | | |
177 |
1
1. appendPrompt : negated conditional → NO_COVERAGE |
if (isAddingPrompt) { |
178 | this.prompt += string; | |
179 | } | |
180 | } catch (BadLocationException e) { | |
181 | LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e); | |
182 | } | |
183 | } | |
184 | ||
185 | /** | |
186 | * NoWrap. | |
187 | */ | |
188 | @Override | |
189 | public boolean getScrollableTracksViewportWidth() { | |
190 |
3
1. getScrollableTracksViewportWidth : changed conditional boundary → NO_COVERAGE 2. getScrollableTracksViewportWidth : negated conditional → NO_COVERAGE 3. getScrollableTracksViewportWidth : replaced boolean return with true for com/jsql/view/swing/shell/AbstractShell::getScrollableTracksViewportWidth → NO_COVERAGE |
return this.getUI().getPreferredSize(this).width <= this.getParent().getSize().width; |
191 | } | |
192 | ||
193 | /** | |
194 | * Cancel every mouse movement processing like drag/drop. | |
195 | */ | |
196 | @Override | |
197 | public synchronized void addMouseMotionListener(MouseMotionListener l) { | |
198 | // Do nothing | |
199 | } | |
200 | ||
201 | /** | |
202 | * Get index of line for current offset (generally cursor position). | |
203 | * @param offset Position on the line | |
204 | * @return Index of the line | |
205 | * @throws BadLocationException | |
206 | */ | |
207 | public int getLineOfOffset(int offset) throws BadLocationException { | |
208 | | |
209 | var errorMsg = "Can't translate offset to line"; | |
210 | var doc = this.getDocument(); | |
211 | | |
212 |
2
1. getLineOfOffset : changed conditional boundary → NO_COVERAGE 2. getLineOfOffset : negated conditional → NO_COVERAGE |
if (offset < 0) { |
213 | throw new BadLocationException(errorMsg, -1); | |
214 |
2
1. getLineOfOffset : negated conditional → NO_COVERAGE 2. getLineOfOffset : changed conditional boundary → NO_COVERAGE |
} else if (offset > doc.getLength()) { |
215 |
1
1. getLineOfOffset : Replaced integer addition with subtraction → NO_COVERAGE |
throw new BadLocationException(errorMsg, doc.getLength() + 1); |
216 | } else { | |
217 | | |
218 | var map = doc.getDefaultRootElement(); | |
219 |
1
1. getLineOfOffset : replaced int return with 0 for com/jsql/view/swing/shell/AbstractShell::getLineOfOffset → NO_COVERAGE |
return map.getElementIndex(offset); |
220 | } | |
221 | } | |
222 | ||
223 | /** | |
224 | * Get position of the beginning of the line. | |
225 | * @param line Index of the line | |
226 | * @return Offset of line | |
227 | * @throws BadLocationException | |
228 | */ | |
229 | public int getLineStartOffset(int line) throws BadLocationException { | |
230 | | |
231 | var map = this.getDocument().getDefaultRootElement(); | |
232 | | |
233 |
2
1. getLineStartOffset : changed conditional boundary → NO_COVERAGE 2. getLineStartOffset : negated conditional → NO_COVERAGE |
if (line < 0) { |
234 | throw new BadLocationException("Negative line", -1); | |
235 |
2
1. getLineStartOffset : changed conditional boundary → NO_COVERAGE 2. getLineStartOffset : negated conditional → NO_COVERAGE |
} else if (line >= map.getElementCount()) { |
236 |
1
1. getLineStartOffset : Replaced integer addition with subtraction → NO_COVERAGE |
throw new BadLocationException("No such line", this.getDocument().getLength() + 1); |
237 | } else { | |
238 | | |
239 | var lineElem = map.getElement(line); | |
240 |
1
1. getLineStartOffset : replaced int return with 0 for com/jsql/view/swing/shell/AbstractShell::getLineStartOffset → NO_COVERAGE |
return lineElem.getStartOffset(); |
241 | } | |
242 | } | |
243 | ||
244 | | |
245 | // Getter and setter | |
246 | | |
247 | public boolean[] getIsEdited() { | |
248 |
1
1. getIsEdited : replaced return value with null for com/jsql/view/swing/shell/AbstractShell::getIsEdited → NO_COVERAGE |
return this.isEdited; |
249 | } | |
250 | ||
251 | public UUID getUuidShell() { | |
252 |
1
1. getUuidShell : replaced return value with null for com/jsql/view/swing/shell/AbstractShell::getUuidShell → NO_COVERAGE |
return this.uuidShell; |
253 | } | |
254 | ||
255 | public String getUrlShell() { | |
256 |
1
1. getUrlShell : replaced return value with "" for com/jsql/view/swing/shell/AbstractShell::getUrlShell → NO_COVERAGE |
return this.urlShell; |
257 | } | |
258 | ||
259 | public String getPrompt() { | |
260 |
1
1. getPrompt : replaced return value with "" for com/jsql/view/swing/shell/AbstractShell::getPrompt → NO_COVERAGE |
return this.prompt; |
261 | } | |
262 | } | |
Mutations | ||
97 |
1.1 |
|
98 |
1.1 |
|
99 |
1.1 |
|
100 |
1.1 |
|
101 |
1.1 |
|
103 |
1.1 |
|
105 |
1.1 |
|
106 |
1.1 |
|
107 |
1.1 |
|
109 |
1.1 |
|
110 |
1.1 |
|
128 |
1.1 |
|
129 |
1.1 |
|
130 |
1.1 |
|
131 |
1.1 |
|
141 |
1.1 |
|
154 |
1.1 |
|
155 |
1.1 |
|
156 |
1.1 |
|
158 |
1.1 |
|
159 |
1.1 |
|
160 |
1.1 |
|
161 |
1.1 |
|
162 |
1.1 |
|
163 |
1.1 |
|
174 |
1.1 |
|
175 |
1.1 |
|
177 |
1.1 |
|
190 |
1.1 2.2 3.3 |
|
212 |
1.1 2.2 |
|
214 |
1.1 2.2 |
|
215 |
1.1 |
|
219 |
1.1 |
|
233 |
1.1 2.2 |
|
235 |
1.1 2.2 |
|
236 |
1.1 |
|
240 |
1.1 |
|
248 |
1.1 |
|
252 |
1.1 |
|
256 |
1.1 |
|
260 |
1.1 |