Add: Added Coverage Screenshots And htlmReport.

This commit is contained in:
GabrieleRadice
2026-06-19 21:50:38 +02:00
parent 6aca188aa3
commit d78b8bbd93
316 changed files with 86329 additions and 0 deletions
@@ -0,0 +1,436 @@
<!DOCTYPE html>
<html id="htmlId">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Coverage Report > ClientLauncherTUI</title>
<style type="text/css">
@import "../../css/coverage.css";
@import "../../css/idea.min.css";
</style>
<script type="text/javascript" src="../../js/highlight.min.js"></script>
<script type="text/javascript" src="../../js/highlightjs-line-numbers.min.js"></script>
</head>
<body>
<div class="content">
<div class="breadCrumbs">
Current scope: <a href="../../index.html">all classes</a>
<span class="separator">|</span>
<a href="../index.html">it.polimi.ingsw.gc14</a>
</div>
<h1>Coverage Summary for Class: ClientLauncherTUI (it.polimi.ingsw.gc14)</h1>
<table class="coverageStats">
<tr>
<th class="name">Class</th>
<th class="coverageStat
">
Class, %
</th>
<th class="coverageStat
">
Method, %
</th>
<th class="coverageStat
">
Branch, %
</th>
<th class="coverageStat
">
Line, %
</th>
</tr>
<tr>
<td class="name">ClientLauncherTUI</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/1)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/11)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/67)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/142)
</span>
</td>
</tr>
</table>
<br/>
<br/>
<pre>
<code class="sourceCode" id="sourceCode">&nbsp;package it.polimi.ingsw.gc14;
&nbsp;
&nbsp;import it.polimi.ingsw.gc14.Controller.ClientController;
&nbsp;import it.polimi.ingsw.gc14.Model.GamePackage.GameStages;
&nbsp;import it.polimi.ingsw.gc14.Network.NetworkConfig;
&nbsp;import it.polimi.ingsw.gc14.Network.InterfaceResolver;
&nbsp;import it.polimi.ingsw.gc14.Network.RMI.Client.RMIClient;
&nbsp;import it.polimi.ingsw.gc14.Network.TCP.Client.TCPClient;
&nbsp;import it.polimi.ingsw.gc14.View.TUI.TUI;
&nbsp;
&nbsp;import org.jline.builtins.Completers.TreeCompleter;
&nbsp;import org.jline.reader.*;
&nbsp;import org.jline.reader.impl.completer.StringsCompleter;
&nbsp;import org.jline.terminal.Terminal;
&nbsp;import org.jline.terminal.TerminalBuilder;
&nbsp;
&nbsp;import java.io.IOException;
&nbsp;import java.io.PrintStream;
&nbsp;import java.nio.charset.StandardCharsets;
&nbsp;
&nbsp;import static org.jline.builtins.Completers.TreeCompleter.node;
&nbsp;
&nbsp;/**
&nbsp; * Entry point for the TUI-based game client.
&nbsp; * Handles login, connects to the server, and drives the JLine-powered command loop.
&nbsp; */
<b class="nc">&nbsp;public class ClientLauncherTUI {</b>
&nbsp; //TODO
&nbsp; private TUI view;
&nbsp; //TODO
&nbsp; private Terminal terminal;
&nbsp; //TODO
&nbsp; private LineReader gameReader;
&nbsp; //TODO
<b class="nc">&nbsp; private String currentUsername = &quot;&quot;;</b>
&nbsp;
&nbsp; private static final String BUILDING_DETAILS =
&nbsp; &quot;ID 0 — Each time you complete a set of 6 different Character cards, take 5 \uD83C\uDF56. (Sets completed before acquiring don&#39;t count.)\n&quot; +
&nbsp; &quot;ID 1 — During the Sustenance Event, pay 1 less \uD83C\uDF56 for each Artist/Inventor/Gatherer in your tribe.\n&quot; +
&nbsp; &quot;ID 2 — During the Shamanic Ritual Event, you do not lose Prestige Points if you have fewer ⭐ than all other players.\n&quot; +
&nbsp; &quot;ID 3 — When you move your Totem to a Food-bonus space, take 1 extra \uD83C\uDF56. If placed last, pay 1 \uD83C\uDF56 normally (no effect).\n&quot; +
&nbsp; &quot;ID 4 — Each time you obtain a pair of identical Inventors (same icon), take 3 \uD83C\uDF56. (Pairs owned before don&#39;t count.)\n&quot; +
&nbsp; &quot;ID 5 — During the Shamanic Ritual Event, your tribe has 3 additional ⭐ icons.\n&quot; +
&nbsp; &quot;ID 6 — During the Shamanic Ritual Event, if you have more ⭐ than any other player, gain double Prestige. Ties still count.\n&quot; +
&nbsp; &quot;ID 7 — During the Hunt Event, take 1 \uD83C\uDF56 and gain 1 extra Prestige Point for each Hunter in your tribe.\n&quot; +
&nbsp; &quot;ID 8 — At end of game, gain double the Prestige Points shown on Builder cards in your tribe.\n&quot; +
&nbsp; &quot;ID 9 — During the Cave Paintings Event, take 1 \uD83C\uDF56 for each Artist in your tribe.\n&quot; +
&nbsp; &quot;ID 10 — At end of game, gain 6 Prestige Points for each complete set of 6 different Character cards.\n&quot; +
&nbsp; &quot;ID 11 — At end of game, gain the indicated Prestige Points for each Character card of the indicated type.\n&quot; +
&nbsp; &quot;ID 12 — After all Totems return to Turn Order (before End of Round), take 1 Character or 1 Building card from the top row (paying its cost).\n&quot; +
&nbsp; &quot;ID 13 — At end of game, gain 25 Prestige Points.&quot;;
&nbsp;
&nbsp; private static final String EVENT_DETAILS =
&nbsp; &quot;SUSTENANCE — Pay 1 \uD83C\uDF56 per Character (Buildings don&#39;t count). If you can&#39;t feed all Characters, lose the\n&quot; +
&nbsp; &quot; Prestige Points shown on the card for each one you couldn&#39;t feed. Each Gatherer gives a 3\uD83C\uDF56 discount.\n&quot; +
&nbsp; &quot; You cannot choose to lose PP to avoid paying Food. Sustenance resolves last if multiple Events occur.\n&quot; +
&nbsp; &quot;\n&quot; +
&nbsp; &quot;HUNT — Take 1 \uD83C\uDF56 and gain the Prestige Points shown on the card for each Hunter in your tribe.\n&quot; +
&nbsp; &quot;\n&quot; +
&nbsp; &quot;SHAMANIC RITUAL — Player with the most \uD83C\uDF1F icons gains the Prestige Points on the card.\n&quot; +
&nbsp; &quot; Player with the fewest \uD83C\uDF1F icons loses those Prestige Points. In case of a tie, all tied players gain/lose.\n&quot; +
&nbsp; &quot;\n&quot; +
&nbsp; &quot;CAVE PAINTINGS — If you have fewer Artists than the top-line threshold, lose the indicated Prestige Points.\n&quot; +
&nbsp; &quot; If you meet the bottom-line threshold, gain the indicated Prestige Points for each Artist in your tribe.&quot;;
&nbsp;
&nbsp; private static final String CHARACTER_DETAILS =
&nbsp; &quot;INVENTORS — At end of game, gain PP equal to (number of Inventors) × (number of different Invention icons).\n&quot; +
&nbsp; &quot; There are 10 different Invention icons.\n&quot; +
&nbsp; &quot;\n&quot; +
&nbsp; &quot;GATHERERS — During Sustenance, each Gatherer gives a 3\uD83C\uDF56 discount on what you owe.\n&quot; +
&nbsp; &quot; Note: you do not take any Food from Gatherers under any circumstances.\n&quot; +
&nbsp; &quot;\n&quot; +
&nbsp; &quot;SHAMANS — Each Shaman shows 13 \uD83C\uDF1F icons. During Shamanic Ritual, majority = gain PP; minority = lose PP.\n&quot; +
&nbsp; &quot;\n&quot; +
&nbsp; &quot;BUILDERS — During the game, each Builder reduces the \uD83C\uDF56 cost of every Building card by the amount\n&quot; +
&nbsp; &quot; shown in the top-right corner. At end of game, each Builder gives the PP shown in the bottom-left corner.\n&quot; +
&nbsp; &quot;\n&quot; +
&nbsp; &quot;ARTISTS — During Cave Paintings, gain or lose PP based on Artist count (see &#39;details events&#39;).\n&quot; +
&nbsp; &quot; At end of game, gain 10 PP for every 2 Artists in your tribe.\n&quot; +
&nbsp; &quot;\n&quot; +
&nbsp; &quot;HUNTERS — Adding a Hunter without the \uD83C\uDF56 icon: nothing. Adding a Hunter WITH the \uD83C\uDF56 icon:\n&quot; +
&nbsp; &quot; immediately take 1 \uD83C\uDF56 for each Hunter in your tribe (with or without the icon).\n&quot; +
&nbsp; &quot; During Hunt Event, take \uD83C\uDF56 and gain PP based on your Hunter count (see &#39;details events&#39;).&quot;;
&nbsp; //TODO
&nbsp; public static void main(String[] args) throws InterruptedException {
<b class="nc">&nbsp; new ClientLauncherTUI().start();</b>
&nbsp; }
&nbsp;
&nbsp; /**
&nbsp; * Starts the TUI client. Creates a JLine terminal, loops through login → game → rematch.
&nbsp; *
&nbsp; * @throws InterruptedException if the thread is interrupted while waiting.
&nbsp; */
&nbsp; private void start() throws InterruptedException {
&nbsp; try {
<b class="nc">&nbsp; System.setOut(new PrintStream(System.out, true, StandardCharsets.UTF_8));</b>
<b class="nc">&nbsp; System.setErr(new PrintStream(System.err, true, StandardCharsets.UTF_8));</b>
<b class="nc">&nbsp; terminal = TerminalBuilder.builder()</b>
<b class="nc">&nbsp; .system(true)</b>
<b class="nc">&nbsp; .encoding(StandardCharsets.UTF_8)</b>
<b class="nc">&nbsp; .build();</b>
&nbsp; } catch (IOException e) {
<b class="nc">&nbsp; System.err.println(&quot;Could not initialise terminal: &quot; + e.getMessage());</b>
&nbsp; return;
&nbsp; }
<b class="nc">&nbsp; terminal.writer().print(&quot;\033[H\033[2J&quot;);</b>
<b class="nc">&nbsp; terminal.writer().flush();</b>
&nbsp;
<b class="nc">&nbsp; view = new TUI(null);</b>
<b class="nc">&nbsp; ClientController controller = new ClientController(view);</b>
<b class="nc">&nbsp; gameReader = buildGameReader();</b>
<b class="nc">&nbsp; view.setLineReader(gameReader);</b>
&nbsp;
&nbsp; while (true) {
<b class="nc">&nbsp; if (!doLogin(controller)) continue;</b>
&nbsp;
<b class="nc">&nbsp; while (controller.getClient() != null) {</b>
<b class="nc">&nbsp; String prompt = buildPrompt(controller);</b>
&nbsp; String line;
&nbsp; try {
<b class="nc">&nbsp; line = gameReader.readLine(prompt);</b>
&nbsp; } catch (UserInterruptException e) {
<b class="nc">&nbsp; safeQuit(controller);</b>
&nbsp; return;
&nbsp; } catch (EndOfFileException e) {
<b class="nc">&nbsp; safeQuit(controller);</b>
&nbsp; return;
&nbsp; }
<b class="nc">&nbsp; if (line == null || line.isBlank()) continue;</b>
<b class="nc">&nbsp; boolean rematch = handleCommand(line.trim(), controller);</b>
<b class="nc">&nbsp; if (rematch) break;</b>
&nbsp; }
&nbsp; }
&nbsp; }
&nbsp; //TODO
&nbsp; private boolean doLogin(ClientController controller) {
<b class="nc">&nbsp; LineReader loginReader = LineReaderBuilder.builder()</b>
<b class="nc">&nbsp; .terminal(terminal)</b>
<b class="nc">&nbsp; .completer(new StringsCompleter(&quot;rmi&quot;, &quot;tcp&quot;))</b>
<b class="nc">&nbsp; .build();</b>
<b class="nc">&nbsp; LineReader ipReader = LineReaderBuilder.builder()</b>
<b class="nc">&nbsp; .terminal(terminal)</b>
<b class="nc">&nbsp; .completer(new StringsCompleter(&quot;localhost&quot;))</b>
<b class="nc">&nbsp; .build();</b>
&nbsp;
&nbsp; String username, networkStr, ip;
&nbsp; int nPlayers;
&nbsp;
&nbsp; try {
<b class="nc">&nbsp; username = loginReader.readLine(&quot;Username: &quot;).trim();</b>
<b class="nc">&nbsp; nPlayers = Integer.parseInt(loginReader.readLine(&quot;Number of players [2-5]: &quot;).trim());</b>
<b class="nc">&nbsp; networkStr = loginReader.readLine(&quot;Network [rmi/tcp]: &quot;).trim().toLowerCase();</b>
<b class="nc">&nbsp; ip = ipReader.readLine(&quot;Server IP [localhost]: &quot;).trim();</b>
<b class="nc">&nbsp; if (ip.isEmpty()) ip = &quot;localhost&quot;;</b>
&nbsp; } catch (UserInterruptException | EndOfFileException e) {
<b class="nc">&nbsp; System.exit(0);</b>
<b class="nc">&nbsp; return false;</b>
&nbsp; } catch (NumberFormatException e) {
<b class="nc">&nbsp; System.out.println(&quot;Invalid number of players.&quot;);</b>
<b class="nc">&nbsp; return false;</b>
&nbsp; }
&nbsp;
<b class="nc">&nbsp; currentUsername = username;</b>
<b class="nc">&nbsp; view.setUsername(username);</b>
<b class="nc">&nbsp; controller.setMyUsername(username);</b>
&nbsp;
<b class="nc">&nbsp; if (networkStr.equals(&quot;rmi&quot;)) {</b>
&nbsp; String myIP;
&nbsp; try {
<b class="nc">&nbsp; myIP = InterfaceResolver.resolveLocalInterface(ip);</b>
&nbsp; } catch (Exception e) {
<b class="nc">&nbsp; System.out.println(&quot;Cannot resolve local interface: &quot; + e.getMessage());</b>
<b class="nc">&nbsp; return false;</b>
&nbsp; }
<b class="nc">&nbsp; System.setProperty(&quot;java.rmi.server.hostname&quot;, myIP);</b>
<b class="nc">&nbsp; RMIClient client = new RMIClient(controller, ip, NetworkConfig.RMI_PORT, myIP);</b>
<b class="nc">&nbsp; ErrorType err = client.connect(username, nPlayers);</b>
<b class="nc">&nbsp; if (err != null) {</b>
<b class="nc">&nbsp; terminal.writer().print(&quot;\033[H\033[2J&quot;);</b>
<b class="nc">&nbsp; terminal.writer().flush();</b>
<b class="nc">&nbsp; System.out.println(&quot;Errore: &quot; + err);</b>
<b class="nc">&nbsp; return false;</b>
&nbsp; }
<b class="nc">&nbsp; System.out.println(&quot;Connected via RMI.&quot;);</b>
<b class="nc">&nbsp; controller.setClient(client);</b>
&nbsp;
<b class="nc">&nbsp; } else if (networkStr.equals(&quot;tcp&quot;)) {</b>
<b class="nc">&nbsp; TCPClient client = new TCPClient(controller, ip, NetworkConfig.TCP_PORT, NetworkConfig.HEARTBEAT_PORT);</b>
<b class="nc">&nbsp; ErrorType err = client.connect(username, nPlayers);</b>
<b class="nc">&nbsp; if (err != null) {</b>
<b class="nc">&nbsp; terminal.writer().print(&quot;\033[H\033[2J&quot;);</b>
<b class="nc">&nbsp; terminal.writer().flush();</b>
<b class="nc">&nbsp; System.out.println(&quot;Errore: &quot; + err);</b>
<b class="nc">&nbsp; return false;</b>
&nbsp; }
<b class="nc">&nbsp; System.out.println(&quot;Connected via TCP.&quot;);</b>
<b class="nc">&nbsp; controller.setClient(client);</b>
&nbsp;
&nbsp; } else {
<b class="nc">&nbsp; System.out.println(&quot;Unknown network type &#39;&quot; + networkStr + &quot;&#39;. Use rmi or tcp.&quot;);</b>
<b class="nc">&nbsp; return false;</b>
&nbsp; }
<b class="nc">&nbsp; return true;</b>
&nbsp; }
&nbsp; //TODO
&nbsp; private LineReader buildGameReader() {
<b class="nc">&nbsp; Completer completer = new TreeCompleter(</b>
<b class="nc">&nbsp; node(&quot;slot&quot;),</b>
<b class="nc">&nbsp; node(&quot;draw&quot;,</b>
<b class="nc">&nbsp; node(&quot;upper&quot;,</b>
<b class="nc">&nbsp; node(&quot;tribe&quot;),</b>
<b class="nc">&nbsp; node(&quot;building&quot;)),</b>
<b class="nc">&nbsp; node(&quot;lower&quot;,</b>
<b class="nc">&nbsp; node(&quot;tribe&quot;),</b>
<b class="nc">&nbsp; node(&quot;building&quot;))),</b>
<b class="nc">&nbsp; node(&quot;totem&quot;),</b>
<b class="nc">&nbsp; node(&quot;skip&quot;),</b>
<b class="nc">&nbsp; node(&quot;render&quot;),</b>
<b class="nc">&nbsp; node(&quot;help&quot;),</b>
<b class="nc">&nbsp; node(&quot;details&quot;, node(&quot;buildings&quot;), node(&quot;events&quot;), node(&quot;characters&quot;)),</b>
<b class="nc">&nbsp; node(&quot;rematch&quot;),</b>
<b class="nc">&nbsp; node(&quot;quit&quot;)</b>
&nbsp; );
<b class="nc">&nbsp; return LineReaderBuilder.builder()</b>
<b class="nc">&nbsp; .terminal(terminal)</b>
<b class="nc">&nbsp; .completer(completer)</b>
<b class="nc">&nbsp; .option(LineReader.Option.DISABLE_EVENT_EXPANSION, true)</b>
<b class="nc">&nbsp; .build();</b>
&nbsp; }
&nbsp; //TODO
&nbsp; private String buildPrompt(ClientController controller) {
<b class="nc">&nbsp; return currentUsername.isEmpty() ? &quot;&gt; &quot; : currentUsername + &quot;&gt; &quot;;</b>
&nbsp; }
&nbsp;
&nbsp; /** Returns {@code true} when the caller should break out of the game loop (rematch requested). */
&nbsp; private boolean handleCommand(String line, ClientController controller) {
<b class="nc">&nbsp; String[] parts = line.split(&quot;\\s+&quot;);</b>
<b class="nc">&nbsp; String cmd = parts[0].toLowerCase();</b>
&nbsp;
<b class="nc">&nbsp; switch (cmd) {</b>
&nbsp; case &quot;slot&quot; -&gt; {
<b class="nc">&nbsp; int pos = parsePos(parts, 1);</b>
<b class="nc">&nbsp; if (pos &gt;= 0) controller.slotChoice(pos);</b>
&nbsp; }
<b class="nc">&nbsp; case &quot;draw&quot; -&gt; handleDraw(parts, controller);</b>
&nbsp; case &quot;totem&quot; -&gt; {
<b class="nc">&nbsp; int pos = parsePos(parts, 1);</b>
<b class="nc">&nbsp; if (pos &gt;= 0) controller.totemChoice(pos);</b>
&nbsp; }
<b class="nc">&nbsp; case &quot;skip&quot; -&gt; controller.skipTurn();</b>
<b class="nc">&nbsp; case &quot;render&quot; -&gt; handleRender(parts);</b>
<b class="nc">&nbsp; case &quot;help&quot; -&gt; gameReader.printAbove(&quot;Commands: slot, draw, totem, skip, render, help, details, rematch, quit&quot;);</b>
&nbsp; case &quot;details&quot; -&gt; {
<b class="nc">&nbsp; if (parts.length &gt; 1 &amp;&amp; parts[1].equalsIgnoreCase(&quot;buildings&quot;))</b>
<b class="nc">&nbsp; gameReader.printAbove(BUILDING_DETAILS);</b>
<b class="nc">&nbsp; else if (parts.length &gt; 1 &amp;&amp; parts[1].equalsIgnoreCase(&quot;events&quot;))</b>
<b class="nc">&nbsp; gameReader.printAbove(EVENT_DETAILS);</b>
<b class="nc">&nbsp; else if (parts.length &gt; 1 &amp;&amp; parts[1].equalsIgnoreCase(&quot;characters&quot;))</b>
<b class="nc">&nbsp; gameReader.printAbove(CHARACTER_DETAILS);</b>
&nbsp; else
<b class="nc">&nbsp; gameReader.printAbove(&quot;Usage: details buildings | details events | details characters&quot;);</b>
&nbsp; }
&nbsp; case &quot;rematch&quot; -&gt; {
<b class="nc">&nbsp; if (controller.getMiniModel() == null</b>
<b class="nc">&nbsp; || !controller.getMiniModel().currentState.getGameStage().equals(GameStages.ENDED)) {</b>
<b class="nc">&nbsp; System.out.println(&quot;Game not ended yet. Use &#39;quit&#39; to disconnect.&quot;);</b>
<b class="nc">&nbsp; return false;</b>
&nbsp; }
<b class="nc">&nbsp; controller.disconnect();</b>
<b class="nc">&nbsp; controller.setClient(null);</b>
<b class="nc">&nbsp; controller.setModel(null);</b>
<b class="nc">&nbsp; return true;</b>
&nbsp; }
&nbsp; case &quot;quit&quot; -&gt; {
<b class="nc">&nbsp; safeQuit(controller);</b>
<b class="nc">&nbsp; System.exit(0);</b>
&nbsp; }
<b class="nc">&nbsp; default -&gt; view.showError(ErrorType.GENERIC_ERROR, &quot;Unknown command: &quot; + cmd);</b>
&nbsp; }
<b class="nc">&nbsp; return false;</b>
&nbsp; }
&nbsp; //TODO
&nbsp; private void handleDraw(String[] parts, ClientController controller) {
<b class="nc">&nbsp; if (parts.length &lt; 4) {</b>
<b class="nc">&nbsp; view.showError(ErrorType.GENERIC_ERROR, &quot;Usage: draw upper|lower tribe|building &lt;pos&gt;&quot;);</b>
&nbsp; return;
&nbsp; }
<b class="nc">&nbsp; boolean upper = parts[1].equalsIgnoreCase(&quot;upper&quot;);</b>
<b class="nc">&nbsp; boolean tribe = parts[2].equalsIgnoreCase(&quot;tribe&quot;);</b>
<b class="nc">&nbsp; int pos = parsePos(parts, 3);</b>
<b class="nc">&nbsp; if (pos &lt; 0) return;</b>
&nbsp;
<b class="nc">&nbsp; if (upper &amp;&amp; tribe) controller.drawUpperTribeCard(pos);</b>
<b class="nc">&nbsp; else if (upper) controller.drawUpperBuildingCard(pos);</b>
<b class="nc">&nbsp; else if (tribe) controller.drawLowerTribeCard(pos);</b>
<b class="nc">&nbsp; else controller.drawLowerBuildingCard(pos);</b>
&nbsp; }
&nbsp; //TODO
&nbsp; private void handleRender(String[] parts) {
<b class="nc">&nbsp; view.renderBoard();</b>
&nbsp; }
&nbsp; //TODO
&nbsp; private int parsePos(String[] parts, int idx) {
<b class="nc">&nbsp; if (parts.length &lt;= idx) {</b>
<b class="nc">&nbsp; view.showError(ErrorType.GENERIC_ERROR, &quot;Missing position argument.&quot;);</b>
<b class="nc">&nbsp; return -1;</b>
&nbsp; }
&nbsp; try {
<b class="nc">&nbsp; return Integer.parseInt(parts[idx]);</b>
&nbsp; } catch (NumberFormatException e) {
<b class="nc">&nbsp; view.showError(ErrorType.GENERIC_ERROR,</b>
&nbsp; &quot;Invalid position &#39;&quot; + parts[idx] + &quot;&#39; — expected integer.&quot;);
<b class="nc">&nbsp; return -1;</b>
&nbsp; }
&nbsp; }
&nbsp; //TODO
&nbsp; private void safeQuit(ClientController controller) {
<b class="nc">&nbsp; if (controller.getClient() != null) controller.disconnect();</b>
&nbsp; }
&nbsp;}
</code>
</pre>
</div>
<script type="text/javascript">
(function() {
var msie = false, msie9 = false;
/*@cc_on
msie = true;
@if (@_jscript_version >= 9)
msie9 = true;
@end
@*/
if (!msie || msie && msie9) {
hljs.highlightAll()
hljs.initLineNumbersOnLoad();
}
})();
</script>
<div class="footer">
<div style="float:right;">generated on 2026-06-14 21:53</div>
</div>
</body>
</html>