I am making a console chat program in java. Which accepts user text input and sends to server, server then broadcast this to all clients. I want to erase the text entered by user, on his console.
I would prefer platform independent solution.
import java.io.*;
class Test
{
public static void main(String args[])
{
System.out.print("you: ");
String t=getString();
System.out.println("We accepted : " + t);
}
static String getString()
{
String s;
try{
BufferedReader bufferRead = new BufferedReader(new InputStreamReader(System.in));
s = bufferRead.readLine();
//int count = 1;
//System.out.print(String.format("\033[%dA",count)); // Move up
//System.out.print("\033[2K"); // Erase line content, works on terminal but not on cmd
for(int i=0;i<s.length();i++)
System.out.print("\b"); //dont know, why it doesnt works??
}
catch(IOException e)
{e.printStackTrace(); s="Error";}
return s;
}
}
This is my interpretation of your question:
I am a client and I type a message, "Hello." Everybody else sees "Hello". However, by typing "Hello" into my console, the message is already there. I don't want to see another "Hello" appear on my screen from the announcer.
Is that the functionality you are trying to achieve? If so, I would suggest that you do not erase the message. If you've printed something to console, it might not be so easy to erase it. Rather you could just never send it in the first place.
The following is what I have generally used for messaging systems in which you don't want the sender to see his message twice:
Conveniently, you have a member object for each person connected to the server, and each member has a name. Hopefully names will be unique. Now, if you ensure that every member gets identified with a name upon connecting to your server, then when you announce the message to everyone, you just make sure that you don't announce it to the sender.
Specifically, I would modify the constructor and the run() method of the announcer class:
String message;
String senderName;
announcer( member x , String msg ) {
senderName = x.name;
message= x.name + " : " + msg ;
}
public void run() {
for ( int i=0 ; i<server.group.size() ; i++ ) {
if ( !server.group.get( i ).name.equals( senderName ) ) {
send( server.group.get( i ).sck );
}
}
}
Now, the message is sent to everyone except the sender, which achieves the equivalent result of sending it to the sender and then erasing it. I'm sorry if you will have to write more code to get everyone's name set up correctly, but I haven't heard of any implementations where duplicate messages were just "erased" from standard output.
Alternatively, if your server.group objects are the same as the member objects you pass into the announcer constructor, then this will work
String message;
member sender;
announcer( member x , String msg ) {
sender = x;
message= x.name + " : " + msg ;
}
public void run() {
for ( int i=0 ; i<server.group.size() ; i++ ) {
if ( server.group.get( i ) != sender ) {
send( server.group.get( i ).sck );
}
}
}
Related
I am a computer science university student working on my first 'big' project outside of class. I'm attempting to read through large text files (2,000 - 3,000 lines of text), line by line with buffered reader. When a keyword from a list of enums is located, I want it to send the current line from buffered reader to its appropriate method to be handled appropriatley.
I have a solution, but I have a feeling in my gut that there is a much better way to handle this situation. Any suggestions or feedback would be greatly appreciated.
Current Solution
I am looping through the the list of enums, then checking if the current enum's toString return is in the current line from buffered reader using the String.contains method.
If the enum is located, the enum is used in a switch statement for the appropriate method call. (I have 13 total cases just wanted to keep the code sample short).
try (BufferedReader reader = new BufferedReader(new FileReader(inputFile.getAbsoluteFile()))){
while ((currentLine = reader.readLine()) != null) {
for (GameFileKeys gameKey : GameFileKeys.values()) {
if (currentLine.contains(gameKey.toString())) {
switch (gameKey) {
case SEAT -> seatAndPlayerAssignment(currentTableArr, currentLine);
case ANTE -> playerJoinLate(currentLine);
}
}
}
}
}
Previous Solution
Originally, I had a nasty list of if statements checking if the current line contained one of the keywords and then handled it appropriatley. Clearly that is far from optimal, but my gut tells me that my current solution is also less than optimal.
try (BufferedReader reader = new BufferedReader(new FileReader(inputFile.getAbsoluteFile()))){
while ((currentLine = reader.readLine()) != null) {
if(currentLine.contains(GameFileKey.SEAT){
seatAndPlayerAssignment(currentTableArr, currentLine);
}
else if(currentLine.contains(GameFileKey.ANTE){
playerJoinLate(currentLine);
}
}
}
Enum Class
In case you need this, or have any general feedback for how I'm implementing my enums.
public enum GameFileKeys {
ANTE("posts ante"),
SEAT("Seat ");
private final String gameKey;
GameFileKeys(String str) {
this.gameKey = str;
}
#Override
public String toString() {
return gameKey;
}
}
I cannot improve over the core of your code: the looping on values() of the enum, performing a String#contains for each enum object’s string, and using a switch. I can make a few minor suggestions.
I suggest you not override the toString method on your enum. The Object#toString method is generally best used only for debugging and logging, not logic or presentation.
Your string passed to constructor of the enum is likely similar to the idea of a display name commonly seen in such enums. The formal enum name (all caps) is used internally within Java, while the display name is used for display to the user or exchanged with external systems. See the Month and DayOfWeek enums as examples offering a getDisplayName method.
Also, an enum should be named in the singular. This avoids confusion with any collections of the enum’s objects.
By the way, looks like you have a stray SPACE in your second enum's argument.
At first I thought it would help to have a list of all the display names, and a map of display name to enum object. However, in the end neither is needed for your purpose. I kept those as they might prove interesting.
public enum GameFileKey
{
ANTE( "posts ante" ),
SEAT( "Seat" );
private String displayName = null;
private static final List < String > allDisplayNames = Arrays.stream( GameFileKey.values() ).map( GameFileKey :: getDisplayName ).toList();
private static final Map < String, GameFileKey > mapOfDisplayNameToGameFileKey = Arrays.stream( GameFileKey.values() ).collect( Collectors.toUnmodifiableMap( GameFileKey :: getDisplayName , Function.identity() ) );
GameFileKey ( String str ) { this.displayName = str; }
public String getDisplayName ( ) { return this.displayName; }
public static GameFileKey forDisplayName ( final String displayName )
{
return
Objects.requireNonNull(
GameFileKey.mapOfDisplayNameToGameFileKey.get( displayName ) ,
"None of the " + GameFileKey.class.getCanonicalName() + " enum objects has a display name of: " + displayName + ". Message # 4dcefee2-4aa2-48cf-bf66-9a4bde02ac37." );
}
public static List < String > allDisplayNames ( ) { return GameFileKey.allDisplayNames; }
}
You can use a stream of the lines of your file being processed. Just FYI, not necessarily better than your code.
public class Demo
{
public static void main ( String[] args )
{
Demo app = new Demo();
app.demo();
}
private void demo ( )
{
try
{
Path path = Demo.getFilePathToRead();
Stream < String > lines = Files.lines( path );
lines.forEach(
line -> {
for ( GameFileKey gameKey : GameFileKey.values() )
{
if ( line.contains( gameKey.getDisplayName() ) )
{
switch ( gameKey )
{
case SEAT -> this.seatAndPlayerAssignment( line );
case ANTE -> this.playerJoinLate( line );
}
}
}
}
);
}
catch ( IOException e )
{
throw new RuntimeException( e );
}
}
private void playerJoinLate ( String line )
{
System.out.println( "line = " + line );
}
private void seatAndPlayerAssignment ( String line )
{
System.out.println( "line = " + line );
}
public static Path getFilePathToRead ( ) throws IOException
{
Path tempFile = Files.createTempFile( "bogus" , ".txt" );
Files.write( tempFile , "apple\nSeat\norange\nposts ante\n".getBytes() );
return tempFile;
}
}
When run:
line = Seat
line = posts ante
This is how my commands are set up:
public void onMessageReceived(MessageReceivedEvent evt) {
//Objects
User objUser = evt.getAuthor();
MessageChannel objMsgCh = evt.getChannel();
Message objMsg = evt.getMessage();
//Commands
if(objMsg.getContentRaw().equalsIgnoreCase(Ref.prefix+"say " + message))
{
StringBuilder message = new StringBuilder();
for(int i = 1; i < command.length; i++) {
if(i == command.length-1) {
message.append(command[i]);
}else {
message.append(command[i] + " ");
}
}
objMsgCh.sendMessage(message.toString()).queue();
objMsg.delete();
return;
}
}
It doesn't reply with anything and I don't understand why.
I am using JDA (Java Discord API).
Respect for your creativity but I think you are missing some basic java knowledge. Here is what I think you are aiming for:
public void onMessageReceived(MessageReceivedEvent event){
if(event.getMessage().getContentRaw().startsWith("!!say")){
event.getChannel().sendMessage(event.getMessage().getContentRaw().substring(6)).queue();
event.getMessage().delete().queue();
}
}
I think the if condition is logical. Get the Messages as String in raw format and looking for the string starting with "!!say". Then send a new Message to the channel where the message was received where the message is the raw message as the string with the first five characters (the "!!say") are cut off. So the Bot is repeating the whole message beside the command tag.
Hope this brings you forward in your mission.
I worked out how to do it.
public void onMessageReceived(MessageReceivedEvent evt) {
//Objects
User objUser = evt.getAuthor();
MessageChannel objMsgCh = evt.getChannel();
Message objMsg = evt.getMessage();
if(objMsg.getContentRaw().startsWith(Ref.prefix+"say"))
{
String words = objMsg.getContentRaw().substring(Ref.prefix.length() + 4);
String more_words = words;
objMsgCh.sendMessage(more_words).queue();
You don't really need String more_words = words;
And I do (Ref.prefix.length() + 4) so it says everything after !!say but make sure to make it create something like String prefix = "!!"; because my may not be able to it with the prefix in the if statement. The + 4 counts every just after the prefix and the space between user input.
For Example:
if(objMsg.getContentRaw().startsWith(Ref.prefix+"urban")) {
String query = objMsg.getContentRaw().substring(Ref.prefix.length() + 6);
Because "urban" has 5 characters you would put 6 to account for the space.
Hope that helps.
If you plan on using the JDA-Utilities the following code will work for creating a command.
public class sayCommand extends Command {
public sayCommand() {
this.help = "!say <message>";
this.aliases = new String[] {"!s"};
this.name = "say";
}
#Override
protected void execute(CommandEvent event) {
event.getChannel().sendMessage(event.getMessage().getContentDisplay().split("\\s+", 2)[1]).queue();
}
}
With only using one line, you could have the bot easily mimic your argument.
I am trying to write customized code within Interactive Brokers' Java API. There are a bunch of methods that get sent to TWS via the eClientSocket object. Two examples are reqIds() and reqMktData(). These are both void methods, so they do not return anything. Instead, they 'activate' methods written within the class that invokes them (in this case, SampleFrame). These methods are also void, in that they don't return any data. Instead, code is written within these methods (nextValidId() and tickPrice() respectively) to handle the data that is sent back from TWS (trader workstation).
I am having trouble creating a modified version of the nextValidId() and tickPrice() methods because reqIds() and reqMktData() don't actually specify these method names in their own code. I therefore cannot write a method called "tickPriceBlackBox()" which is called from within reqMktData(), or from within a copy of reqMktData() called reqMktDataBlackBox(). Again, there is no specific code within reqMktData() that can be modified to call a specific tickPriceBlackBox() method. It's as if code within TWS itself is hardwired to call the tickPrice() method, making it impossible for me to create a new method for returning price information.
Can anyone explain what is going on, or how to create a solution?
Here's some code:
void onReqMktData() {//requests market data from TWS / Interactive Brokers
// run m_orderDlg
m_orderDlg.init("Mkt Data Options", true, "Market Data Options", m_mktDataOptions);
m_orderDlg.show();
if( !m_orderDlg.m_rc ) {
return;
}
m_mktDataOptions = m_orderDlg.getOptions();
// req mkt data
m_client.reqMktData( m_orderDlg.m_id, m_orderDlg.m_contract,
m_orderDlg.m_genericTicks, m_orderDlg.m_snapshotMktData, m_mktDataOptions);
}
//Here is the reqMktData() method
public synchronized void reqMktData(int tickerId, Contract contract,
String genericTickList, boolean snapshot, List mktDataOptions) {
if (!m_connected) {
error(EClientErrors.NO_VALID_ID, EClientErrors.NOT_CONNECTED, "");
return;
}
if (m_serverVersion < MIN_SERVER_VER_SNAPSHOT_MKT_DATA && snapshot) {
error(tickerId, EClientErrors.UPDATE_TWS,
" It does not support snapshot market data requests.");
return;
}
if (m_serverVersion < MIN_SERVER_VER_UNDER_COMP) {
if (contract.m_underComp != null) {
error(tickerId, EClientErrors.UPDATE_TWS,
" It does not support delta-neutral orders.");
return;
}
}
if (m_serverVersion < MIN_SERVER_VER_REQ_MKT_DATA_CONID) {
if (contract.m_conId > 0) {
error(tickerId, EClientErrors.UPDATE_TWS,
" It does not support conId parameter.");
return;
}
}
if (m_serverVersion < MIN_SERVER_VER_TRADING_CLASS) {
if (!IsEmpty(contract.m_tradingClass)) {
error(tickerId, EClientErrors.UPDATE_TWS,
" It does not support tradingClass parameter in reqMarketData.");
return;
}
}
final int VERSION = 11;
try {
// send req mkt data msg
send(REQ_MKT_DATA);
send(VERSION);
send(tickerId);
// send contract fields
if (m_serverVersion >= MIN_SERVER_VER_REQ_MKT_DATA_CONID) {
send(contract.m_conId);
}
send(contract.m_symbol);
send(contract.m_secType);
send(contract.m_expiry);
send(contract.m_strike);
send(contract.m_right);
if (m_serverVersion >= 15) {
send(contract.m_multiplier);
}
send(contract.m_exchange);
if (m_serverVersion >= 14) {
send(contract.m_primaryExch);
}
send(contract.m_currency);
if(m_serverVersion >= 2) {
send( contract.m_localSymbol);
}
if(m_serverVersion >= MIN_SERVER_VER_TRADING_CLASS) {
send( contract.m_tradingClass);
}
if(m_serverVersion >= 8 && BAG_SEC_TYPE.equalsIgnoreCase(contract.m_secType)) {
if ( contract.m_comboLegs == null ) {
send( 0);
}
else {
send( contract.m_comboLegs.size());
ComboLeg comboLeg;
for (int i=0; i < contract.m_comboLegs.size(); i ++) {
comboLeg = contract.m_comboLegs.get(i);
send( comboLeg.m_conId);
send( comboLeg.m_ratio);
send( comboLeg.m_action);
send( comboLeg.m_exchange);
}
}
}
if (m_serverVersion >= MIN_SERVER_VER_UNDER_COMP) {
if (contract.m_underComp != null) {
UnderComp underComp = contract.m_underComp;
send( true);
send( underComp.m_conId);
send( underComp.m_delta);
send( underComp.m_price);
}
else {
send( false);
}
}
if (m_serverVersion >= 31) {
/*
* Note: Even though SHORTABLE tick type supported only
* starting server version 33 it would be relatively
* expensive to expose this restriction here.
*
* Therefore we are relying on TWS doing validation.
*/
send( genericTickList);
}
if (m_serverVersion >= MIN_SERVER_VER_SNAPSHOT_MKT_DATA) {
send (snapshot);
}
// send mktDataOptions parameter
if(m_serverVersion >= MIN_SERVER_VER_LINKING) {
StringBuilder mktDataOptionsStr = new StringBuilder();
int mktDataOptionsCount = mktDataOptions == null ? 0 : mktDataOptions.size();
if( mktDataOptionsCount > 0) {
for( int i = 0; i < mktDataOptionsCount; ++i) {
TagValue tagValue = (TagValue)mktDataOptions.get(i);
mktDataOptionsStr.append( tagValue.m_tag);
mktDataOptionsStr.append( "=");
mktDataOptionsStr.append( tagValue.m_value);
mktDataOptionsStr.append( ";");
}
}
send( mktDataOptionsStr.toString());
}
}
catch( Exception e) {
error( tickerId, EClientErrors.FAIL_SEND_REQMKT, "" + e);
close();
}
}
//The key piece of this code, REQ_MKT_DATA, leads to a final int variable within the EClientSocket.java object, equal to 1. tickPrice() is not mentioned anywhere.
//This method provides stock price, but doesn't return a value. You have to put executable code within this one method. I cannot duplicate and change the name of this method (tickprice();) because none of my accessible code calls it, to my knowledge. It feels as if TWS is calling tickPrice from its end.
public void tickPrice( int tickerId, int field, double price, int canAutoExecute) {
// received price tick
String msg = EWrapperMsgGenerator.tickPrice( tickerId, field, price, canAutoExecute);
m_tickers.add( msg );
}
The tickPrice method is called from EReader which gets created in EClientSocket which knows the EWrapper implementation.
Basically you call the socket reqMktData method and it will send it to TWS. EReader will see the response on the socket as a tickPrice message and will send it to the Wrapper implementation.
If you want to handle it yourself then you do it inside the tickPrice method. It could be just as simple as passing the data to a method you define.
public void tickPrice( int tickerId, int field, double price, int canAutoExecute) {
handleTick(tickerId,field,price);
}
And then write your own handleTick method
Finally may have found an answer. I'm new to Java...these appear to be callback methods. A callback method receives information from some other source. Because the method is part of an object in OOP, the returned information (stock info in this case) is returned into the callback method. Any other code that is contained within the callback method is executed when the method is replied to.
I am still not clear on how these methods are activated if they haven't been specifically executed in the code on my machine. Does Interactive Broker's know to feed information back to this method inside my Java program? Seems logical.
public class ClientServer {
public static void main (String[] args){
Object[] selectioValues = {"Server "," Client"};
String initialSection = "Server";
Object selection = JOptionPane.showInputDialog(null,"login As:","Client server", JOptionPane.QUESTION_MESSAGE,null , selectioValues , initialSection );
**if(selection.equals("Server"))**{
server srv = new server();
srv.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
srv.startRunning();
}
**else if (selection.equals("Client"))**{
String IPServer = JOptionPane.showInputDialog("enter IP:");
Client capsa;
capsa = new Client(IPServer);
capsa.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
capsa.startRunning();
}
}
}
The code in bold doesn't work. When I added breakpoints to check whether the compiler is going inside IF then it was not. Please explain me why IF condition is not running.
Your possible selection values are "Server " and " Client" but you compare to "Server" and "Client". The strings are not equal because of the leading/trailing spaces.
My code is to add RSS feeds to a list - and the code originally was only to pull one feed from the first position in a list, and add this object to another list.
This was the original code:
public static List<Feed> getFeedsFromXml(String xml) {
Pattern feedPattern = Pattern.compile("<feed>\\s*<name>\\s*([^<]*)</name>\\s*<uri>\\s*([^<]*)</uri>\\s*</feed>");
Matcher feedMatch = feedPattern.matcher(xml);
while (feedMatch.find()) {
String feedName = feedMatch.group(1);
String feedURI = feedMatch.group(2);
feeds.add(new Feed(feedName, feedURI));
}
return feeds;
}
#POST
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
public String addXmlFeed() throws IOException
{
int i = 0;
String stringXml = "<feed><name>SMH Top Headlines</name><uri>http://feeds.smh.com.au/rssheadlines/top.xml</uri></feed><feed><name>UTS Library News</name>";
getFeedsFromXml(stringXml);
Feed f = (Feed) feeds.get(0);
feedList.add(f);
String handler = "You have successfully added: \n";
String xmlStringReply = "" + f + "\n";
feedList.save(feedFile);
return handler + xmlStringReply;
}
Everything was going well, and then I decided to implement a for loop for handling the adding of more than one feed to the list, and I tried the following (only the code for the second method in question):
#POST
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
public String addXmlFeed() throws IOException
{
int i = 0;
String stringXml = "<feed><name>SMH Top Headlines</name><uri>http://feeds.smh.com.au/rssheadlines/top.xml</uri></feed><feed><name>UTS Library News</name>";
getFeedsFromXml(stringXml);
for (Feed feed: feeds)
{
Feed f = (Feed) feeds.get(i++);
feedList.add(f);
String handler = "You have successfully added: \n";
String xmlStringReply = "" + f + "\n";
}
feedList.save(feedFile);
return handler + xmlStringReply;
}
Now I'm sure this is a basic problem, but now in the line:
return handler + xmlStringReply;
handler and xmlStringReply cannot be resolved to a variable as they are within the FOR LOOP.
Is there any easy way around this?
The scope of those 2 variables is limited to the for loop. To access them outside the loop, you need to increase their scope by declaring them before the loop:
String handler = "";
String xmlStringReply = "";
for (Feed f: feeds) {
feedList.add(f);
handler = "You have successfully added: \n";
xmlStringReply = "" + f + "\n";
}
feedList.save(feedFile);
return handler + xmlStringReply;
Also, your current code overwrites the value of your strings at each loop, whereas you probably meant to concatenate the values. In that case, you could use a StringBuilder instead of string concatenation:
StringBuilder xmlStringReply = new StringBuilder("You have successfully added: \n");
for (Feed f: feeds) {
feedList.add(f);
xmlStringReply.append(f + "\n");
}
feedList.save(feedFile);
return xmlStringReply.toString();
The question you need to answer is "what do I want to return if I add several feeds ?".
Maybe you'd like to return "You have successfully added : feed1 feed2 feed3\n"
In that case, the code is :
StringBuilder response = new StringBuilder( "You have successfully added: ");
for (Feed feed: feeds)
{
feedList.add(feed);
response.append(f.toString()).append(" ");
}
feedList.save(feedFile);
return response.toString();
By the way, your feedand fvariables are just the same and redondant !
Don't write :
int i = 0;
for (Feed feed: feeds)
{
Feed f = (Feed) feeds.get(i++);
feedList.add(f);
}
but
for (Feed feed: feeds)
{
feedList.add(feed);
}
You need to accumulate the result into a variable. I am using StringBuilder because it makes string concatenation efficient.
#POST
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
public String addXmlFeed() throws IOException
{
String stringXml = "<feed><name>SMH Top Headlines</name><uri>http://feeds.smh.com.au/rssheadlines/top.xml</uri></feed><feed><name>UTS Library News</name>";
getFeedsFromXml(stringXml);
StringBuilder replyBuilder = new StringBuilder("You have successfully added: \n");
for (Feed feed : feeds)
{
feedList.add(feed);
String xmlStringReply = feed + "\n";
reployBuilder.append(xmlStringReply);
}
feedList.save(feedFile);
return replyBuilder.toString();
}
Because, now they became out of scope.
Beside the original error -- you can easily fix that using other suggestions, I would like to suggest that you should not make feeds as instance variable. I can see your method getFeedsFromXml() is returning the list. So, I think it would have been better if you define that variable inside that method. And then, call the method like,
List<Feed> feeds = getFeedsFromXml(stringXml);
Or in case, this doesn't give you the desired behaviour, then you should rename the method to something, loadFeedsFromXml(). Making that as instance variable may result in threading issues.
Now, trying to improve on your looping,
StringBuilder xmlStringReply = new StringBuilder("You have successfully added: \n");
for (Feed feed: feeds) {
feedList.add(feed);
xmlStringReply.append(f + "\n");
}
feedList.save(feedFile);
return xmlStringReply.toString();
Moreover, I found that your feedList is also a instance variable. And this again can cause threading issues, as it doesn't sound immutable or stateless. Synchronising the methods will give you performance issues. See if you can make it local to this method. A rule of thumb is to keep variable scope as narrow as possible.
A good rule of thumb is to view scope like this:
{ //This is a constructor
int i;
} // This is a deconstructor
anything that is created / instantiated between the curlies only lives inside the curlies. Whenever your working with variables and loops:
for(int i = 0; i < 10; i++){
//some code here
} // after this curly i is no longer in scope or accessible.