I'm working on a basic RPC design. From a listener, I retrieve a desired RPC like:
ArrayList<String> params = new ArrayList<String>();
params.add(Node.getLocal().getHostname());
params.add("test");
RPC rawRPC = StorageControl.getRPC(StorageControl.RPC_HelloMaster, params));
My StorageControl class is pretty simple right now,
public class StorageControl {
public StorageControl(){
availableRPCs.put(RPC_HelloMaster, new RPC("[%s] Hello, Master. [%s]"));
}
public static final String RPC_HelloMaster = "helloMaster";
private static String MasterHostname;
public static String getMaster(){ return MasterHostname; }
public static void setMaster(String host){ MasterHostname = host; }
private static Map<String, RPC> availableRPCs = new HashMap<String, RPC>();
public static RPC getRPC(String key, ArrayList<String> params) {
RPC rawRPC = availableRPCs.get(key);
// This is what fails
for (String param : params){
rawRPC.msg = String.format(rawRPC.msg, param);
}
return rawRPC;
}
}
RPC is just a simple class, containing a single variable, msg
So, the idea is that I want to retrieve RPCs, that may have a variable number of variables that need substituted. Is there a more elegant way (that actually works) to do this? What I have now fails with a MissingFormatArgumentException, I assume because the first loop doesn't attempt to replace beyond the 1st variable.
At first we need to know how format works,
String x = String.format("%s %s","hello","world");
then x will have "hello world". But if we do this
String x = String.format("%s %s","hello");
It will give you a illegal argument exception because there are not enough arguments to replace.
So you need to pass all arguments at once. Now variable arguments actually array of args. So you can do this.
String stringToFormat = "%s %s %s";
String[] ags = {"hello","world","gg"};
stringToFormat = String.format(stringToFormat,ags);
System.out.println(stringToFormat);
In your case, you can just do this without loop
rawRPC.msg = String.format(rawRPC.msg, params.toArray(new String[params.size()]));
Related
is it possible to create a class and have a String ... attribute that takes as many or as little strings as you put?
example:
please excuse my rough pseudocode, this is for java.
//this is the method:
public void getXXXX(String ...) {
//random code executes in a loop with as many as strings that are inputted
}
//this code calls it
getXXXX("Benjamin","Jordan","Steve")
getXXXX("Pengu","No")
getXXXX("hi")
Yes, what you entered will more or less work, you just need a parameter name after your type.
class StringDecorator {
public static String join(final String... strings) {
final var builder = new StringBuilder();
for (final var string : strings) {
builder.append(string);
}
return builder.toString();
}
}
Then invoke this somewhere
StringDecorator.join("Hello, ", "World!"); // "Hello, World!"
Here is my first program using JSON. It is almost finished, only needs to be sorted by the "updated_at" value, and then return the sorted result in the form "name" + "updated_at". Anyone can help me with this / write code?
How can i do this?
public class Main {
public static void main(String[] args) throws Exception {
ArrayWithAllRepos arrayWithAllRepos = new ArrayWithAllRepos();
String dataFromPage1 = URLReader.readUrl(AllegroURL.URL_1);
String dataFromPage2 = URLReader.readUrl(AllegroURL.URL_2);
String dataFromPage3 = URLReader.readUrl(AllegroURL.URL_3);
JSONArray jsonArrayWithDataFromPage1 = new JSONArray(dataFromPage1);
JSONArray jsonArrayWithDataFromPage2 = new JSONArray(dataFromPage2);
JSONArray jsonArrayWithDataFromPage3 = new JSONArray(dataFromPage3);
arrayWithAllRepos.addToJsonToArray(jsonArrayWithDataFromPage1);
arrayWithAllRepos.addToJsonToArray(jsonArrayWithDataFromPage2);
arrayWithAllRepos.addToJsonToArray(jsonArrayWithDataFromPage3);
arrayWithAllRepos.printArray(arrayWithAllRepos.getJsonArray());
}
}
public class AllegroURL {
public static final String URL_1 = "https://api.github.com/users/allegro/repos?pagelen=1000&page=1";
public static final String URL_2 = "https://api.github.com/users/allegro/repos?pagelen=1000&page=2";
public static final String URL_3 = "https://api.github.com/users/allegro/repos?pagelen=1000&page=3";
}
I dont know the class ArrayWithAllRepos, but you can create Comparator which gets two json objects, and compare them with this value (you can see a lot of examples in google, here is one https://www.geeksforgeeks.org/comparator-interface-java/)
and use .stream().map() methods on the collection, in order to change the output (again, you can see a lot of examples in google, here is one https://javarevisited.blogspot.com/2018/05/java-8-filter-map-collect-stream-example.html)
If your collection does not support stream(), you can do a simple for, and create new json with you values.
Since
public static String requestMethodExecution(String objectName, String className, String methodName, Object...
params) {
return String.format("%s,%s,%s,%s", objectName, className, methodName, Arrays.toString(params));
}
returns a String, and if you would, for example, call the method like this:
requestMethodExecution("foo","bar","fooBar",2.0,3.0,"Hello");
You'd get a String like this: foo,bar,fooBar,[2.0,3.0,Hello]
I would love to iterate over that Array, but I can't since it is a String.
Reason behind this is this method: (I just started with reflection, so I do not know how else to do it)
public static Class[] getParameterType(String ...params) throws ClassNotFoundException {
Class[] paramTypes = new Class[params.length];
for(int i=0; i<params.length;i++){
Class paramClass = Class.forName(params[i]);
if (paramClass == Double.class) {
paramTypes[i] = (double.class);
} else if (paramClass == Integer.class) {
paramTypes[i] = (int.class);
} else {
paramTypes[i] = paramClass;
}
}
return paramTypes;
}
So far I have only come up with a very dirty way:
public static String[] getParams(String message){
int indexOfParamStart = message.indexOf("[");
int indexOfParamEnd = message.indexOf("]")+1;
String[] splitMessage = message.substring(indexOfParamStart, indexOfParamEnd).replaceAll("\\[", "")
.replaceAll("]", "").replaceAll(" ","").split(",");
return splitMessage;
}
Edit: Thanks for looking into this! Since some of you are asking what I am trying to achieve, here is a bit more explanation:
I want to implement a simple request/reply protocol which allows remote method invocation (and I do not want to use java RMI...)
So I listen for requests whose structure can be seen at the requestMethodExecution example.
There I have all the relevant information to call the Method upon my class, so to invoke the method I need it's arguments (and their value) and I do not know how to access them from the given String.
The others are easy with Class c = Class.forName(className); etc..
Edit#2:
My question is not about a simple regex, so why close it? The title already states a different subject, I am getting a bit salty here...
See this this question for using RegEx to extract the array body from the outer string (by the square brackets), and then you can simply use String.split(",") to split the array body into array items.
I have a java class in which I store an Enum.(shown at the bottom of this question) In this enum, I have a method named toCommaSeperatedString() who returns a comma separated String of the enums values. I am using a StringBuilder after reading some information on performance in this question here.
Is the way I am converting this enum's values into a commaSeperatedString the most efficient way of doing so, and if so, what would be the most efficient way to remove the extra comma at the last char of the String?
For example, my method returns 123, 456, however I would prefer 123, 456. If I wanted to return PROPERTY1, PROPERTY2 I could easily use Apache Commons library StringUtils.join(), however, I need to get one level lower by calling the getValue method when I am iterating through the String array.
public class TypeEnum {
public enum validTypes {
PROPERTY1("123"),
PROPERTY2("456");
private String value;
validTypes(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public static boolean contains(String type) {
for (validTypes msgType : validTypes.values()) {
if (msgType.value.equals(type)) {
return true;
}
}
return false;
}
public static String toCommaSeperatedString() {
StringBuilder commaSeperatedValidMsgTypes = new StringBuilder();
for(validTypes msgType : validTypes.values()) {
commaSeperatedValidMsgTypes.append(msgType.getValue() + ", ");
}
return commaSeperatedValidMsgTypes.toString();
}
}
}
I wouldn't worry much about efficiency. It's simple enough to do this that it will be fast, provided you don't do it in a crazy way. If this is the most significant performance bottleneck in your code, I would be amazed.
I'd do it something like this:
return Arrays.stream(TypeEnum.values())
.map(t -> t.value)
.collect(Collectors.joining(','));
Cache it if you want; but that's probably not going to make a huge difference.
A common pattern for the trailing comma problem I see is something like
String[] values = {"A", "B", "C"};
boolean is_first = true;
StringBuilder commaSeperatedValidMsgTypes = new StringBuilder();
for(String value : values){
if(is_first){
is_first = false;
}
else{
commaSeperatedValidMsgTypes.append(',');
}
commaSeperatedValidMsgTypes.append(value);
}
System.out.println(commaSeperatedValidMsgTypes.toString());
which results in
A,B,C
Combining this with the answers about using a static block to initialize a static final field will probably give the best performance.
The most efficient code is code that doesn't run. This answer can't ever change, so run that code as you have it once when creating the enums. Take the hit once, return the calculated answer every other time somebody asks for it. The savings in doing that would be far greater in the long term over worrying about how specifically to construct the string, so use whatever is clearest to you (write code for humans to read).
For example:
public enum ValidTypes {
PROPERTY1("123"),
PROPERTY2("345");
private final static String asString = calculateString();
private final String value;
private static String calculateString() {
return // Do your work here.
}
ValidTypes(final String value) {
this.value = value;
}
public static String toCommaSeparatedString() {
return asString;
}
}
If you have to call this static method thousand and thousand of times on a short period, you may worry about performance and you should first check that this has a performance cost.
The JVM performs at runtime many optimizations.
So finally you could write more complex code without added value.
Anyway, the actual thing that you should do is storing the String returned by toCommaSeperatedString and returned the same instance.
Enum are constant values. So caching them is not a problem.
You could use a static initializer that values a static String field.
About the , character, just remove it after the loop.
public enum validTypes {
PROPERTY1("123"), PROPERTY2("456");
private static String valueSeparatedByComma;
static {
StringBuilder commaSeperatedValidMsgTypes = new StringBuilder();
for (validTypes msgType : validTypes.values()) {
commaSeperatedValidMsgTypes.append(msgType.getValue());
commaSeperatedValidMsgTypes.append(",");
}
commaSeperatedValidMsgTypes.deleteCharAt
(commaSeperatedValidMsgTypes.length()-1);
valueSeparatedByComma = commaSeperatedValidMsgTypes.toString();
}
public static String getvalueSeparatedByComma() {
return valueSeparatedByComma;
}
I usually add a static method on the enum class itself:
public enum Animal {
CAT, DOG, LION;
public static String possibleValues() {
return Arrays.stream(Animal.values())
.map(Enum::toString)
.collect(Collectors.joining(","));
}
}
So I can use it like String possibleValues = Animal.possibleValues();
Identifier error either has ** or is in bold. I also do not know if the rest of the program will work.
import javax.swing.JOptionPane;
public class Multi
{
public static void main (String args[]);
**public static String dataIn (Stringinfo)**
{
String words = "Your hypotenuse is?";
String word1 = "Your second side is?";
String word2 = "Your thrid side is?";
int a = Integer.parseInt (words);
int b = Integer.parseInt (word1);
int c = Integer.parseInt (word2);
if ((a*a+b*b)== (c*c))
{
System.out.println ("Right triangle") ;
}
}
public static String dataIn (String info)
{
String answer = JOptionPane.showInputDialog(info); return answer;
}
}
You are declaring a method main like you would in an interface without a body. However, you can't do this outside an interface so main must have a body
public static void main (String args[]) { }
or be removed.
In addition to what #clcto said about your main method not having a body, there is another problem. You need to specify a data type while adding parameters just like when you create variables.
public static String dataIn (String Stringinfo)
Here String is the data type, just like in your other variables. Change String to be whatever fits your needs best.