code cleanup

This commit is contained in:
John Grosh 2018-12-04 23:37:45 -05:00
parent c88979a6dd
commit c0f0d45138
16 changed files with 212 additions and 324 deletions

View file

@ -1,24 +1,26 @@
[download]: https://github.com/jagrosh/MusicBot/releases/latest
[release]: https://img.shields.io/github/release/jagrosh/MusicBot.svg
[setup]: https://github.com/jagrosh/MusicBot/wiki/Setup
[rpi]: https://github.com/jagrosh/MusicBot/wiki/JMusicBot-on-Raspberry-Pi
[features]: https://github.com/jagrosh/MusicBot/projects/1
## General Troubleshooting ## General Troubleshooting
Please check off these steps before suggesting a feature or reporting a bug: Please check off these steps before suggesting a feature or reporting a bug:
- [ ] I am running the [latest version][download]: [![Release][release]][download] - [ ] I am running the [latest version][download]: [![Release][release]][download]
- [ ] I have followed all instructions on the [setup page][setup] (and if applicable, [Raspberry Pi][rpi]) - [ ] I have followed all instructions on the [setup page][setup] (and if applicable, [Raspberry Pi][rpi])
- [ ] I have read through the [planned and suggested features][features] - [ ] I have read through the [planned and suggested features][features]
## Issue ## Issue
### Issue Type ### Issue Type
- [ ] Bug Report - [ ] Bug Report
- [ ] Feature Request - [ ] Feature Request
### Description ### Description
Replace this with a description of your bug or feature request. Please provide screenshots and logs as applicable. Replace this with a description of your bug or feature request. Please provide screenshots and logs as applicable.
[download]: https://github.com/jagrosh/MusicBot/releases/latest
[release]: https://img.shields.io/github/release/jagrosh/MusicBot.svg
[setup]: https://github.com/jagrosh/MusicBot/wiki/Setup
[rpi]: https://github.com/jagrosh/MusicBot/wiki/JMusicBot-on-Raspberry-Pi
[features]: https://github.com/jagrosh/MusicBot/projects/1

View file

@ -5,32 +5,32 @@
[![License](https://img.shields.io/github/license/jagrosh/MusicBot.svg)](https://github.com/jagrosh/MusicBot/blob/master/LICENSE) [![License](https://img.shields.io/github/license/jagrosh/MusicBot.svg)](https://github.com/jagrosh/MusicBot/blob/master/LICENSE)
[![Discord](https://discordapp.com/api/guilds/147698382092238848/widget.png)](https://discord.gg/0p9LSGoRLu6Pet0k) [![Discord](https://discordapp.com/api/guilds/147698382092238848/widget.png)](https://discord.gg/0p9LSGoRLu6Pet0k)
# JMusicBot ## JMusicBot
A Discord music bot with a clean interface, and that is easy to set up and run yourself A Discord music bot with a clean interface, and that is easy to set up and run yourself
[![Setup](http://i.imgur.com/VvXYp5j.png)](https://github.com/jagrosh/MusicBot/wiki/Setup) [![Setup](http://i.imgur.com/VvXYp5j.png)](https://github.com/jagrosh/MusicBot/wiki/Setup)
# Features ## Features
* Easy to run (just make sure Java is installed, and run!) * Easy to run (just make sure Java is installed, and run!)
* Fast loading of songs * Fast loading of songs
* No external keys needed (besides a Discord Bot token) * No external keys needed (besides a Discord Bot token)
* Smooth playback * Smooth playback
* Server-specific setup for the "DJ" role that can moderate the music * Server-specific setup for the "DJ" role that can moderate the music
* Clean and beautiful menus * Clean and beautiful menus
* Channel-topic playback bar * Channel-topic playback bar
* Supports many sites, including Youtube, Soundcloud, and more * Supports many sites, including Youtube, Soundcloud, and more
* Supports many online radio/streams * Supports many online radio/streams
* Supports local files * Supports local files
* Playlist support (both web/youtube, and local) * Playlist support (both web/youtube, and local)
# Setup ## Setup
Please see the [Setup Page](https://github.com/jagrosh/MusicBot/wiki/Setup) in the wiki to run this bot yourself! Please see the [Setup Page](https://github.com/jagrosh/MusicBot/wiki/Setup) in the wiki to run this bot yourself!
# Questions/Suggestions/Bug Reports ## Questions/Suggestions/Bug Reports
**Please read the [Suggested/Planned Features List](https://github.com/jagrosh/MusicBot/projects/1) before suggesting a feature**. If you'd like to suggest changes to how the bot functions, recommend more customization options, or report bugs, feel free to either open an [Issue](https://github.com/jagrosh/MusicBot/issues) on this repository, or join [my Discord server](https://discord.gg/0p9LSGoRLu6Pet0k). (Note: I will not accept any feature requests that will require additional API keys, nor any non-music features). If you like this bot, be sure to add a star to the libraries that make this possible: [**JDA**](https://github.com/DV8FromTheWorld/JDA) and [**lavaplayer**](https://github.com/sedmelluq/lavaplayer) **Please read the [Suggested/Planned Features List](https://github.com/jagrosh/MusicBot/projects/1) before suggesting a feature**. If you'd like to suggest changes to how the bot functions, recommend more customization options, or report bugs, feel free to either open an [Issue](https://github.com/jagrosh/MusicBot/issues) on this repository, or join [my Discord server](https://discord.gg/0p9LSGoRLu6Pet0k). (Note: I will not accept any feature requests that will require additional API keys, nor any non-music features). If you like this bot, be sure to add a star to the libraries that make this possible: [**JDA**](https://github.com/DV8FromTheWorld/JDA) and [**lavaplayer**](https://github.com/sedmelluq/lavaplayer)
# Example ## Example
![Example](https://i.imgur.com/tevrtKt.png) ![Example](https://i.imgur.com/tevrtKt.png)
# Editing ## Editing
This bot (and the source code here) is not meant to be edited. The main purpose of having the source public is to show the capabilities of the libraries, and to allow others to understand how the bot works. There are many requirements and dependencies required to edit and compile it, and there will not be support provided for people looking to make changes on their own. Instead, consider making a feature request (see the above section). If you still choose to make edits, please do so in accordance with the Apache 2.0 License. This bot (and the source code here) is not meant to be edited. The main purpose of having the source public is to show the capabilities of the libraries, and to allow others to understand how the bot works. There are many requirements and dependencies required to edit and compile it, and there will not be support provided for people looking to make changes on their own. Instead, consider making a feature request (see the above section). If you still choose to make edits, please do so in accordance with the Apache 2.0 License.

View file

@ -128,7 +128,7 @@ public class JMusicBot
new PlaylistCmd(bot), new PlaylistCmd(bot),
new SetavatarCmd(), new SetavatarCmd(),
new SetgameCmd(), new SetgameCmd(),
new SetnameCmd(bot), new SetnameCmd(),
new SetstatusCmd(), new SetstatusCmd(),
new ShutdownCmd(bot) new ShutdownCmd(bot)
); );

View file

@ -58,10 +58,9 @@ public class Listener extends ListenerAdapter
{ {
String defpl = bot.getSettingsManager().getSettings(guild).getDefaultPlaylist(); String defpl = bot.getSettingsManager().getSettings(guild).getDefaultPlaylist();
VoiceChannel vc = bot.getSettingsManager().getSettings(guild).getVoiceChannel(guild); VoiceChannel vc = bot.getSettingsManager().getSettings(guild).getVoiceChannel(guild);
if(defpl!=null && vc!=null) if(defpl!=null && vc!=null && bot.getPlayerManager().setUpHandler(guild).playFromDefault())
{ {
if(bot.getPlayerManager().setUpHandler(guild).playFromDefault()) guild.getAudioManager().openAudioConnection(vc);
guild.getAudioManager().openAudioConnection(vc);
} }
} }
catch(Exception ex) {} catch(Exception ex) {}

View file

@ -29,8 +29,6 @@ import java.util.Set;
import com.jagrosh.jmusicbot.queue.FairQueue; import com.jagrosh.jmusicbot.queue.FairQueue;
import com.jagrosh.jmusicbot.settings.Settings; import com.jagrosh.jmusicbot.settings.Settings;
import com.jagrosh.jmusicbot.utils.FormatUtil; import com.jagrosh.jmusicbot.utils.FormatUtil;
import static com.jagrosh.jmusicbot.utils.FormatUtil.formatTime;
import static com.jagrosh.jmusicbot.utils.FormatUtil.volumeIcon;
import com.sedmelluq.discord.lavaplayer.source.youtube.YoutubeAudioTrack; import com.sedmelluq.discord.lavaplayer.source.youtube.YoutubeAudioTrack;
import net.dv8tion.jda.core.EmbedBuilder; import net.dv8tion.jda.core.EmbedBuilder;
import net.dv8tion.jda.core.JDA; import net.dv8tion.jda.core.JDA;
@ -256,10 +254,10 @@ public class AudioHandler extends AudioEventAdapter implements AudioSendHandler
title = track.getInfo().uri; title = track.getInfo().uri;
return "**"+title+"** ["+(userid==0 ? "autoplay" : "<@"+userid+">")+"]" return "**"+title+"** ["+(userid==0 ? "autoplay" : "<@"+userid+">")+"]"
+ "\n"+(audioPlayer.isPaused()?"\u23F8":"\u25B6")+" " + "\n"+(audioPlayer.isPaused()?"\u23F8":"\u25B6")+" "
+"["+formatTime(track.getDuration())+"] " +"["+FormatUtil.formatTime(track.getDuration())+"] "
+volumeIcon(audioPlayer.getVolume()); +FormatUtil.volumeIcon(audioPlayer.getVolume());
} }
else return "No music playing \u23F9 " + volumeIcon(audioPlayer.getVolume()); else return "No music playing \u23F9 " + FormatUtil.volumeIcon(audioPlayer.getVolume());
} }
// Audio Send Handler methods // Audio Send Handler methods

View file

@ -18,13 +18,11 @@ package com.jagrosh.jmusicbot.audio;
import com.jagrosh.jmusicbot.Bot; import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.entities.Pair; import com.jagrosh.jmusicbot.entities.Pair;
import com.jagrosh.jmusicbot.settings.Settings; import com.jagrosh.jmusicbot.settings.Settings;
import com.jagrosh.jmusicbot.utils.FormatUtil;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack; import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import net.dv8tion.jda.core.MessageBuilder;
import net.dv8tion.jda.core.Permission; import net.dv8tion.jda.core.Permission;
import net.dv8tion.jda.core.entities.Game; import net.dv8tion.jda.core.entities.Game;
import net.dv8tion.jda.core.entities.Guild; import net.dv8tion.jda.core.entities.Guild;

View file

@ -16,23 +16,16 @@
package com.jagrosh.jmusicbot.commands.dj; package com.jagrosh.jmusicbot.commands.dj;
import com.jagrosh.jdautilities.command.CommandEvent; import com.jagrosh.jdautilities.command.CommandEvent;
import com.jagrosh.jdautilities.menu.ButtonMenu;
import com.jagrosh.jmusicbot.Bot; import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.audio.AudioHandler; import com.jagrosh.jmusicbot.audio.AudioHandler;
import com.jagrosh.jmusicbot.audio.QueuedTrack; import com.jagrosh.jmusicbot.audio.QueuedTrack;
import com.jagrosh.jmusicbot.commands.DJCommand; import com.jagrosh.jmusicbot.commands.DJCommand;
import com.jagrosh.jmusicbot.commands.music.PlayCmd;
import static com.jagrosh.jmusicbot.commands.music.PlayCmd.CANCEL;
import static com.jagrosh.jmusicbot.commands.music.PlayCmd.LOAD;
import com.jagrosh.jmusicbot.utils.FormatUtil; import com.jagrosh.jmusicbot.utils.FormatUtil;
import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler; import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler;
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException; import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist; import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack; import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import java.util.concurrent.TimeUnit;
import net.dv8tion.jda.core.Permission;
import net.dv8tion.jda.core.entities.Message; import net.dv8tion.jda.core.entities.Message;
import net.dv8tion.jda.core.exceptions.PermissionException;
/** /**
* *
@ -69,9 +62,9 @@ public class PlaynextCmd extends DJCommand
private class ResultHandler implements AudioLoadResultHandler private class ResultHandler implements AudioLoadResultHandler
{ {
final Message m; private final Message m;
final CommandEvent event; private final CommandEvent event;
final boolean ytsearch; private final boolean ytsearch;
private ResultHandler(Message m, CommandEvent event, boolean ytsearch) private ResultHandler(Message m, CommandEvent event, boolean ytsearch)
{ {

View file

@ -63,5 +63,5 @@ public class RepeatCmd extends DJCommand
} }
@Override @Override
public void doCommand(CommandEvent event) {} public void doCommand(CommandEvent event) { /* Intentionally Empty */ }
} }

View file

@ -17,7 +17,6 @@ package com.jagrosh.jmusicbot.commands.general;
import com.jagrosh.jdautilities.command.Command; import com.jagrosh.jdautilities.command.Command;
import com.jagrosh.jdautilities.command.CommandEvent; import com.jagrosh.jdautilities.command.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.settings.Settings; import com.jagrosh.jmusicbot.settings.Settings;
import net.dv8tion.jda.core.EmbedBuilder; import net.dv8tion.jda.core.EmbedBuilder;
import net.dv8tion.jda.core.MessageBuilder; import net.dv8tion.jda.core.MessageBuilder;

View file

@ -99,9 +99,9 @@ public class PlayCmd extends MusicCommand
private class ResultHandler implements AudioLoadResultHandler private class ResultHandler implements AudioLoadResultHandler
{ {
final Message m; private final Message m;
final CommandEvent event; private final CommandEvent event;
final boolean ytsearch; private final boolean ytsearch;
private ResultHandler(Message m, CommandEvent event, boolean ytsearch) private ResultHandler(Message m, CommandEvent event, boolean ytsearch)
{ {

View file

@ -73,8 +73,9 @@ public class SearchCmd extends MusicCommand
private class ResultHandler implements AudioLoadResultHandler private class ResultHandler implements AudioLoadResultHandler
{ {
final Message m; private final Message m;
final CommandEvent event; private final CommandEvent event;
private ResultHandler(Message m, CommandEvent event) private ResultHandler(Message m, CommandEvent event)
{ {
this.m = m; this.m = m;

View file

@ -27,8 +27,11 @@ import com.jagrosh.jmusicbot.commands.OwnerCommand;
*/ */
public class EvalCmd extends OwnerCommand public class EvalCmd extends OwnerCommand
{ {
private final Bot bot;
public EvalCmd(Bot bot) public EvalCmd(Bot bot)
{ {
this.bot = bot;
this.name = "eval"; this.name = "eval";
this.help = "evaluates nashorn code"; this.help = "evaluates nashorn code";
this.guildOnly = false; this.guildOnly = false;
@ -38,6 +41,7 @@ public class EvalCmd extends OwnerCommand
protected void execute(CommandEvent event) protected void execute(CommandEvent event)
{ {
ScriptEngine se = new ScriptEngineManager().getEngineByName("Nashorn"); ScriptEngine se = new ScriptEngineManager().getEngineByName("Nashorn");
se.put("bot", bot);
se.put("event", event); se.put("event", event);
se.put("jda", event.getJDA()); se.put("jda", event.getJDA());
se.put("guild", event.getGuild()); se.put("guild", event.getGuild());

View file

@ -26,7 +26,7 @@ import net.dv8tion.jda.core.exceptions.RateLimitedException;
*/ */
public class SetnameCmd extends OwnerCommand public class SetnameCmd extends OwnerCommand
{ {
public SetnameCmd(Bot bot) public SetnameCmd()
{ {
this.name = "setname"; this.name = "setname";
this.help = "sets the name of the bot"; this.help = "sets the name of the bot";
@ -37,13 +37,18 @@ public class SetnameCmd extends OwnerCommand
@Override @Override
protected void execute(CommandEvent event) protected void execute(CommandEvent event)
{ {
try { try
{
String oldname = event.getSelfUser().getName(); String oldname = event.getSelfUser().getName();
event.getSelfUser().getManager().setName(event.getArgs()).complete(false); event.getSelfUser().getManager().setName(event.getArgs()).complete(false);
event.reply(event.getClient().getSuccess()+" Name changed from `"+oldname+"` to `"+event.getArgs()+"`"); event.reply(event.getClient().getSuccess()+" Name changed from `"+oldname+"` to `"+event.getArgs()+"`");
} catch(RateLimitedException e) { }
catch(RateLimitedException e)
{
event.reply(event.getClient().getError()+" Name can only be changed twice per hour!"); event.reply(event.getClient().getError()+" Name can only be changed twice per hour!");
} catch(Exception e) { }
catch(Exception e)
{
event.reply(event.getClient().getError()+" That name is not valid!"); event.reply(event.getClient().getError()+" That name is not valid!");
} }
} }

View file

@ -51,7 +51,7 @@ public class GUI extends JFrame
setVisible(true); setVisible(true);
addWindowListener(new WindowListener() addWindowListener(new WindowListener()
{ {
@Override public void windowOpened(WindowEvent e) {} @Override public void windowOpened(WindowEvent e) { /* unused */ }
@Override public void windowClosing(WindowEvent e) @Override public void windowClosing(WindowEvent e)
{ {
try try
@ -63,11 +63,11 @@ public class GUI extends JFrame
System.exit(0); System.exit(0);
} }
} }
@Override public void windowClosed(WindowEvent e) {} @Override public void windowClosed(WindowEvent e) { /* unused */ }
@Override public void windowIconified(WindowEvent e) {} @Override public void windowIconified(WindowEvent e) { /* unused */ }
@Override public void windowDeiconified(WindowEvent e) {} @Override public void windowDeiconified(WindowEvent e) { /* unused */ }
@Override public void windowActivated(WindowEvent e) {} @Override public void windowActivated(WindowEvent e) { /* unused */ }
@Override public void windowDeactivated(WindowEvent e) {} @Override public void windowDeactivated(WindowEvent e) { /* unused */ }
}); });
} }
} }

View file

@ -1,111 +0,0 @@
/*
* Copyright 2017 John Grosh <john.a.grosh@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jagrosh.jmusicbot.gui;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.util.List;
import javax.swing.DefaultListModel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.audio.AudioHandler;
import com.jagrosh.jmusicbot.utils.FormatUtil;
import net.dv8tion.jda.core.entities.Guild;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class GuildsPanel extends JPanel
{
private final Bot bot;
private final JList guildList;
private final JTextArea guildQueue;
private int index = -1;
public GuildsPanel(Bot bot)
{
super();
super.setLayout(new GridBagLayout());
this.bot = bot;
guildList = new JList();
guildQueue = new JTextArea();
guildList.setModel(new DefaultListModel());
guildList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
guildList.setFixedCellHeight(20);
guildList.setPreferredSize(new Dimension(100,300));
guildQueue.setPreferredSize(new Dimension(300,300));
guildQueue.setEditable(false);
JScrollPane pane = new JScrollPane();
JScrollPane pane2 = new JScrollPane();
pane.setViewportView(guildList);
pane2.setViewportView(guildQueue);
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
c.anchor = GridBagConstraints.LINE_START;
c.gridx = 0;
c.gridwidth = 1;
super.add(pane, c);
c.gridx = 1;
c.gridwidth = 3;
super.add(pane2, c);
//bot.registerPanel(this);
guildList.addListSelectionListener((ListSelectionEvent e) -> {
index = guildList.getSelectedIndex();
//bot.updatePanel();
});
}
public void updateList(List<Guild> guilds)
{
String[] strs = new String[guilds.size()];
for(int i=0; i<guilds.size(); i++)
strs[i] = guilds.get(i).getName();
guildList.setListData(strs);
}
public int getIndex()
{
return guildList.getSelectedIndex();
}
/*public void updatePanel(AudioHandler handler) {
StringBuilder builder = new StringBuilder("Now Playing: ");
if(handler==null || handler.getCurrentTrack()==null)
{
builder.append("nothing");
}
else
{
builder.append(handler.getCurrentTrack().getTrack().getInfo().title)
.append(" [")
.append(FormatUtil.formatTime(handler.getCurrentTrack().getTrack().getDuration()))
.append("]\n");
for(int i=0; i<handler.getQueue().size(); i++)
builder.append("\n").append(i+1).append(". ").append(handler.getQueue().get(i).getTrack().getInfo().title);
}
guildQueue.setText(builder.toString());
guildQueue.updateUI();
}*/
}

View file

@ -1,147 +1,147 @@
/* /*
* Copyright 2017 John Grosh <john.a.grosh@gmail.com>. * Copyright 2017 John Grosh <john.a.grosh@gmail.com>.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.jagrosh.jmusicbot.gui; package com.jagrosh.jmusicbot.gui;
import java.awt.*; import java.awt.*;
import java.io.*; import java.io.*;
import java.util.*; import java.util.*;
import java.util.List; import java.util.List;
import javax.swing.*; import javax.swing.*;
/** /**
* *
* @author Lawrence Dol * @author Lawrence Dol
*/ */
public class TextAreaOutputStream extends OutputStream { public class TextAreaOutputStream extends OutputStream {
// ************************************************************************************************* // *************************************************************************************************
// INSTANCE MEMBERS // INSTANCE MEMBERS
// ************************************************************************************************* // *************************************************************************************************
private byte[] oneByte; // array for write(int val); private byte[] oneByte; // array for write(int val);
private Appender appender; // most recent action private Appender appender; // most recent action
public TextAreaOutputStream(JTextArea txtara) { public TextAreaOutputStream(JTextArea txtara) {
this(txtara,1000); this(txtara,1000);
} }
public TextAreaOutputStream(JTextArea txtara, int maxlin) { public TextAreaOutputStream(JTextArea txtara, int maxlin) {
if(maxlin<1) { throw new IllegalArgumentException("TextAreaOutputStream maximum lines must be positive (value="+maxlin+")"); } if(maxlin<1) { throw new IllegalArgumentException("TextAreaOutputStream maximum lines must be positive (value="+maxlin+")"); }
oneByte=new byte[1]; oneByte=new byte[1];
appender=new Appender(txtara,maxlin); appender=new Appender(txtara,maxlin);
} }
/** Clear the current console text area. */ /** Clear the current console text area. */
public synchronized void clear() { public synchronized void clear() {
if(appender!=null) { appender.clear(); } if(appender!=null) { appender.clear(); }
} }
@Override @Override
public synchronized void close() { public synchronized void close() {
appender=null; appender=null;
} }
@Override @Override
public synchronized void flush() { public synchronized void flush() {
} /* empty */
}
@Override
public synchronized void write(int val) { @Override
oneByte[0]=(byte)val; public synchronized void write(int val) {
write(oneByte,0,1); oneByte[0]=(byte)val;
} write(oneByte,0,1);
}
@Override
public synchronized void write(byte[] ba) { @Override
write(ba,0,ba.length); public synchronized void write(byte[] ba) {
} write(ba,0,ba.length);
}
@Override
public synchronized void write(byte[] ba,int str,int len) { @Override
if(appender!=null) { appender.append(bytesToString(ba,str,len)); } public synchronized void write(byte[] ba,int str,int len) {
} if(appender!=null) { appender.append(bytesToString(ba,str,len)); }
}
//@edu.umd.cs.findbugs.annotations.SuppressWarnings("DM_DEFAULT_ENCODING")
static private String bytesToString(byte[] ba, int str, int len) { //@edu.umd.cs.findbugs.annotations.SuppressWarnings("DM_DEFAULT_ENCODING")
try { return new String(ba,str,len,"UTF-8"); } catch(UnsupportedEncodingException thr) { return new String(ba,str,len); } // all JVMs are required to support UTF-8 static private String bytesToString(byte[] ba, int str, int len) {
} try { return new String(ba,str,len,"UTF-8"); } catch(UnsupportedEncodingException thr) { return new String(ba,str,len); } // all JVMs are required to support UTF-8
}
// *************************************************************************************************
// STATIC MEMBERS // *************************************************************************************************
// ************************************************************************************************* // STATIC MEMBERS
// *************************************************************************************************
static class Appender
implements Runnable static class Appender
{ implements Runnable
private final JTextArea textArea; {
private final int maxLines; // maximum lines allowed in text area static private final String EOL1="\n";
private final LinkedList<Integer> lengths; // length of lines within text area static private final String EOL2=System.getProperty("line.separator",EOL1);
private final List<String> values; // values waiting to be appended
private final JTextArea textArea;
private int curLength; // length of current line private final int maxLines; // maximum lines allowed in text area
private boolean clear; private final LinkedList<Integer> lengths; // length of lines within text area
private boolean queue; private final List<String> values; // values waiting to be appended
Appender(JTextArea txtara, int maxlin) { private int curLength; // length of current line
textArea =txtara; private boolean clear;
maxLines =maxlin; private boolean queue;
lengths =new LinkedList<>();
values =new ArrayList<>(); Appender(JTextArea txtara, int maxlin) {
textArea =txtara;
curLength=0; maxLines =maxlin;
clear =false; lengths =new LinkedList<>();
queue =true; values =new ArrayList<>();
}
curLength=0;
synchronized void append(String val) { clear =false;
values.add(val); queue =true;
if(queue) { queue=false; EventQueue.invokeLater(this); } }
}
private synchronized void append(String val) {
synchronized void clear() { values.add(val);
clear=true; if(queue) { queue=false; EventQueue.invokeLater(this); }
curLength=0; }
lengths.clear();
values.clear(); private synchronized void clear() {
if(queue) { queue=false; EventQueue.invokeLater(this); } clear=true;
} curLength=0;
lengths.clear();
// MUST BE THE ONLY METHOD THAT TOUCHES textArea! values.clear();
@Override if(queue) { queue=false; EventQueue.invokeLater(this); }
public synchronized void run() { }
if(clear) { textArea.setText(""); }
values.stream().map((val) -> { // MUST BE THE ONLY METHOD THAT TOUCHES textArea!
curLength+=val.length(); @Override
return val; public synchronized void run() {
}).map((val) -> { if(clear) { textArea.setText(""); }
if(val.endsWith(EOL1) || val.endsWith(EOL2)) { values.stream().map((val) -> {
if(lengths.size()>=maxLines) { textArea.replaceRange("",0,lengths.removeFirst()); } curLength+=val.length();
lengths.addLast(curLength); return val;
curLength=0; }).map((val) -> {
} if(val.endsWith(EOL1) || val.endsWith(EOL2)) {
return val; if(lengths.size()>=maxLines) { textArea.replaceRange("",0,lengths.removeFirst()); }
}).forEach((val) -> { lengths.addLast(curLength);
textArea.append(val); curLength=0;
}); }
values.clear(); return val;
clear =false; }).forEach((val) -> {
queue =true; textArea.append(val);
} });
values.clear();
static private final String EOL1="\n"; clear =false;
static private final String EOL2=System.getProperty("line.separator",EOL1); queue =true;
} }
}
} /* END PUBLIC CLASS */ } /* END PUBLIC CLASS */