I am working on a program, where I want users to define a simple functions like
randomInt(0,10)
or
randomString(10)
instead of static arguments. What is the best way to parse and process such functions ?
I have not found any examples of such problem, the parser does not have to be ultra-efficient, it will not be called often, but mainly I want to focus on good code readability and scalability.
Example of user input:
"This is user randomString(5) and he is randomInt(18,60) years old!"
Expected output(s):
"This is user phiob and he is 45 years old!"
"This is user sdfrt and he is 30 years old!"
One option is to use Spring SPEL. But it forces you to change the expression a little and use Spring library:
The expression can look like this:
'This is user ' + randomString(5) + ' and he is ' + randomInt(18,60) + ' years old!'
or this:
This is user #{randomString(5)} and he is #{randomInt(18,60)} years old!
or you can implement your own by having a custom TemplateParserContext.
And here is the code:
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class SomeTest {
#Test
public void test() {
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(
"This is user #{randomString(5)} and he is #{randomInt(18,60)} years old!",
new TemplateParserContext() );
//alternative
//Expression exp = parser.parseExpression(
// "'This is user ' + randomString(5) + ' and he is ' + randomInt(18,60) + ' years old!'");
// String message = (String) exp.getValue( new StandardEvaluationContext(this) );
String message = (String) exp.getValue( new StandardEvaluationContext(this) );
}
public String randomString(int i) {
return "rs-" + i;
}
public String randomInt(int i, int j) {
return "ri-" + i + ":" + "j";
}
}
Whatever object you pass to StandardEvaluationContext should have those methods. I put them in the same class that also runs the expression.
You could use something such as the following:
Warning, I haven't tested it. Just something to get started with
public String parseInput(String input){
String[] inputArray = input.split(" ");
String output = "";
for(String in : inputArray){ //run through each word of the user input
if(in.contains("randomString(")){ //if the user is calling randomString
String params = in.replace("randomString(", ""); //strip away function to get to params
params = in.replace("(", ""); //strip away function to get to params
String[] paramsArray = params.split(","); //these are string integers, and could be converted
//send off these split apart parameters to your randomString method
String out = randomString(paramsArray); //method parses string integers, outputs string
output += out + " ";
}else if(in.contains("randomInt(")){ //if the user is calling randomInt
String params = in.replace("randomInt(", ""); //strip away function to get to params
params = in.replace("(", ""); //strip away function to get to params
String[] paramsArray = params.split(","); //these are string integers, and could be converted
//send off these split apart parameters to your randomInt method
String out = randomInt(paramsArray); //method parses string integers, outputs string
output += out + " ";
}else{ //if the user is just entering text
output += in + " "; //concat the output with what the user wrote plus a space
}
}
return output;
}
Related
I am working on a selenium script where 3 values are being fetched and store in String.
String one = "19292";
String two = "Abc";
String three = "def";
I want it to send text as (one + two + three) but all of them is having double quotes. So end result should be "19292""Abc""def"
How can I do this ?
I have tried using the escape mechanism using back slash, but whenever I use it rather than fetching the string value it prints the text.
For Eg :
\"one\" prints "one" rather than "19292"
Try sth like this:
field.sendKeys("\"" + one + "\"\"" + two + "\"\"" + three + "\"");
I just checked with selenium and it works. The input which goes to field is :
"19292""Abc""def"
Or if you don't know the number of Strings then below is the method which will convert in quotes format.
public static void main(String[] args) {
String one = "19292";
String two = "Abc";
String three = "def";
System.out.println(surroundWithDoubleQuotes(one, two, three));
}
public static String surroundWithDoubleQuotes(String... input) {
if(input == null || input.length == 0) {
return "";
}
StringBuilder builder = new StringBuilder();
for(String arg : input) {
builder.append("\"\"");
builder.append(arg);
}
builder.append("\"");
builder.deleteCharAt(0);
return builder.toString();
}
Try this:
String text = "\"" + one + "\"\"" + two + "\"\"" + three + "\"";
Try:
String str = "\""+one+"\"\""+two+"\"\""+three+"\"";
System.out.println(str);
This will print "19292""Abc""def"
If your usecase is to pass the resultant string as "19292" "Abc" "def", then definitely the following Strings declarations won't help the purpose:
String one = "19292";
String two = "Abc";
String three = "def";
Instead you need to do the following:
String one = " \"19292\" ";
String two = " \"Abc\" ";
String three = " \"def\" ";
An example to send the strings as **"19292" "Abc" "def"**with the textbox of Google Home Page you can use the following Locator Strategies:
Code Block:
public class A_demo
{
public static void main(String[] args) throws Exception
{
String one = " \"19292\" ";
String two = " \"Abc\" ";
String three = " \"def\" ";
System.setProperty("webdriver.chrome.driver", "C:\\Utility\\BrowserDrivers\\chromedriver.exe");
ChromeOptions options = new ChromeOptions();
options.addArguments("start-maximized");
options.setExperimentalOption("excludeSwitches", Collections.singletonList("enable-automation"));
options.setExperimentalOption("useAutomationExtension", false);
WebDriver driver = new ChromeDriver(options);
driver.get("https://www.google.com/");
new WebDriverWait(driver, 20).until(ExpectedConditions.elementToBeClickable(By.name("q"))).sendKeys(one + two + three);
}
}
Browser Snapshot:
I've posted about letters earlier, but this is an another topic, I have a json response that contain 2 objects, from and to , from is what to change, and to is what it will be changed to .
My code is :
// for example, the EnteredText is "ab b test a b" .
EnteredString = EnteredText.getText().toString();
for (int i = 0; i < m_jArry.length(); i++) {
JSONObject jo_inside = m_jArry.getJSONObject(i);
String Original = jo_inside.getString("from");
String To = jo_inside.getString("to");
if(isMethodConvertingIn){
EnteredString = EnteredString.replace(" ","_");
EnteredString = EnteredString.replace(Original,To + " ");
} else {
EnteredString = EnteredString.replace("_"," ");
EnteredString = EnteredString.replace(To + " ", Original);
}
}
LoadingProgress.setVisibility(View.GONE);
SetResultText(EnteredString);
ShowResultCardView();
For example, the json response is :
{
"Response":[
{"from":"a","to":"bhduh"},{"from":"b","to":"eieja"},{"from":"tes","to":"neesj"}
]
}
String.replace() method won't work here, because first it will replace a to bhduh, then b to eieja, BUT here's the problem, it will convert b in bhduh to eieja, which i don't want to.
I want to perfectly convert the letters and "words" in the String according the Json, but that what i'm failing at .
New Code :
if(m_jArry.length() > 0){
HashMap<String, String> m_li;
EnteredString = EnteredText.getText().toString();
Log.i("TestAf_","Before Converting: " + EnteredString);
HashMap<String,String> replacements = new HashMap<String,String>();
for (int i = 0; i < m_jArry.length(); i++) {
JSONObject jo_inside = m_jArry.getJSONObject(i);
String Original = jo_inside.getString("from");
String To = jo_inside.getString("to");
if(isMethodConvertingIn){
//EnteredString = EnteredString.replace(" ","_");
replacements.put(Original,To);
Log.i("TestAf_","From: " + Original + " - To: " + To + " - Loop: " + i);
//EnteredString = EnteredString.replace(" ","_");
//EnteredString = EnteredString.replace(Original,To + " ");
} else {
EnteredString = EnteredString.replace("_"," ");
EnteredString = EnteredString.replace("'" + To + "'", Original);
}
}
Log.i("TestAf_","After Converting: " + replaceTokens(EnteredString,replacements));
// Replace Logic Here
// When Finish, Do :
LoadingProgress.setVisibility(View.GONE);
SetResultText(replaceTokens(EnteredString,replacements));
ShowResultCardView();
Output :
10-10 19:51:19.757 12113-12113/? I/TestAf_: Before Converting: ab a ba
10-10 19:51:19.757 12113-12113/? I/TestAf_: From: a - To: bhduh - Loop: 0
10-10 19:51:19.757 12113-12113/? I/TestAf_: From: b - To: eieja - Loop: 1
10-10 19:51:19.757 12113-12113/? I/TestAf_: From: o - To: neesj - Loop: 2
10-10 19:51:19.758 12113-12113/? I/TestAf_: After Converting: ab a ba
You question would be clearer if you gave the expected output for the function.
Assuming it is: ab b test a b >>>> bhduheieja eieja neesjt bhduh eieja
then see the following, the key point in the Javadoc being "This will not repeat"
http://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apache/commons/lang3/StringUtils.html#replaceEach(java.lang.String,%20java.lang.String[],%20java.lang.String[])
Replaces all occurrences of Strings within another String.
A null reference passed to this method is a no-op, or if any "search
string" or "string to replace" is null, that replace will be ignored.
This will not repeat. For repeating replaces, call the overloaded
method.
Example 1
import org.apache.commons.lang3.StringUtils;
public class StringReplacer {
public static void main(String[] args) {
String input = "ab b test a b";
String output = StringUtils.replaceEach(input, new String[] { "a", "b", "tes" },
new String[] { "bhduh", "eieja", "neesj" });
System.out.println(input + " >>>> " + output);
}
}
Example 2
import org.apache.commons.lang3.StringUtils;
public class StringReplacer {
public static void main(String[] args) {
String input = "this is a test string with foo";
String output = StringUtils.replaceEach(input, new String[] { "a", "foo" },
new String[] { "foo", "bar"});
System.out.println(input + " >>>> " + output);
}
}
Try following:
Solution 1:
Traverse the String characters one by one and move the new String to a new StringBuffer or StringBuilder, then call toString() to get the result. This will need you to implement string matching algorithm.
Solution 2 (Using Regex):
For this, you must know the domain of your string. For example, it is [a-zA-Z] then other arbitrary characters (not part of domain) can be used for intermediate step. First replace the actual characters with arbitrary one then arbitrary ones with the target. In example below, [!##] are the arbitrary characters. These can be any random \uxxxx value as well.
String input = "a-b-c";
String output = input.replaceAll("[a]", "!").replaceAll("[b]", "#").replaceAll("[c]", "#");
output = output.replaceAll("[!]", "bcd").replaceAll("[#]", "cde").replaceAll("[#]", "def");
System.out.println("input: " + input);
System.out.println("Expected: bcd-cde-def");
System.out.println("Actual: " + output);
Your issue is quite common. To sum things up :
String test = "this is a test string with foo";
System.out.println(test.replace("a", "foo").replace("foo", "bar"));
Gives : this is bar test string with bar
Expected by you : this is foo test string with bar
You can use StrSubstitutor from Apache Commons Lang
But first you will have to inject placeholders in your string :
String test = "this is a test string with foo";
Map<String, String> valuesMap = new HashMap<>();
valuesMap.put("a", "foo");
valuesMap.put("foo", "bar");
String testWithPlaceholder = test;
// Preparing the placeholders
for (String value : valuesMap.keySet())
{
testWithPlaceholder = testWithPlaceholder.replace(value, "${"+value+"}");
}
And then, use StrSubstitutor
System.out.println(StrSubstitutor.replace(testWithPlaceholder, valuesMap));
It gives : this is foo test string with bar
Here is an method which is strictly just Java. I tried not to use any Java 8 methods here.
public static String translate(final String str, List<String> from, List<String> to, int index) {
StringBuilder components = new StringBuilder();
String token, replace;
int p;
if (index < from.size()) {
token = from.get(index);
replace = to.get(index);
p = 0;
for (int i = str.indexOf(token, p); i != -1; i = str.indexOf(token, p)) {
if (i != p) {
components.append(translate(str.substring(p, i), from, to, index + 1));
}
components.append(replace);
p = i + token.length();
}
return components.append(translate(str.substring(p), from, to, index + 1)).toString();
}
return str;
}
public static String translate(final String str, List<String> from, List<String> to) {
if (null == str) {
return null;
}
return translate(str, from, to, 0);
}
Sample test program
public static void main(String []args) {
String EnteredString = "aa hjkyu batesh a";
List<String> from = new ArrayList<>(Arrays.asList("a", "b", "tes"));
List<String> to = new ArrayList<>(Arrays.asList("bhduh", "eieja", "neesj"));
System.out.println(translate(EnteredString, from, to));
}
Output:
bhduhbhduh hjkyu eiejabhduhneesjh bhduh
Explaination
The algorithm is recursive, and it simply does the following
If a pattern found in the string matches a pattern in the from list
if there is any string before that pattern, apply the algorithm to that string
replace the found pattern with the corresponding pattern in the to list
append the replacement to the new string
discard the pattern in the from list and repeat the algorithm for the rest of the string
Otherwise append the rest of the string to the new string
You could use split like:
String[] pieces = jsonResponse.split("},{");
then you just parse the from and to in each piece and apply them with replace() then put the string back together again. (and please get your capitalization of your variables/methods right - makes it very hard to read the way you have it)
Apache Commons StringUtils::replaceEach does this.
String[] froms = new String[] {"a", "b"};
String[] tos = new String[] {"b","c"};
String result = StringUtils.replaceEach("ab", froms, tos);
// result is "bc"
Why not keep it very simple (if the JSON is always in same format, EG: from the same system). Instead of replacing from with to, replace the entire markup:
replace "from":"*from*" with "from":"*to*"
Why not just change the actual "to" and "from" labels? That way, you don't run into a situation where "bhudh" becomes "eieja". Just do a string replace on "from" and "to".
I was wondering if someone can show me how to use the format method for Java Strings.
For instance If I want the width of all my output to be the same
For instance, Suppose I always want my output to be the same
Name = Bob
Age = 27
Occupation = Student
Status = Single
In this example, all the output are neatly formatted under each other; How would I accomplish this with the format method.
System.out.println(String.format("%-20s= %s" , "label", "content" ));
Where %s is a placeholder for you string.
The '-' makes the result left-justified.
20 is the width of the first string
The output looks like this:
label = content
As a reference I recommend Javadoc on formatter syntax
If you want a minimum of 4 characters, for instance,
System.out.println(String.format("%4d", 5));
// Results in " 5", minimum of 4 characters
To answer your updated question you can do
String[] lines = ("Name = Bob\n" +
"Age = 27\n" +
"Occupation = Student\n" +
"Status = Single").split("\n");
for (String line : lines) {
String[] parts = line.split(" = +");
System.out.printf("%-19s %s%n", parts[0] + " =", parts[1]);
}
prints
Name = Bob
Age = 27
Occupation = Student
Status = Single
EDIT: This is an extremely primitive answer but I can't delete it because it was accepted. See the answers below for a better solution though
Why not just generate a whitespace string dynamically to insert into the statement.
So if you want them all to start on the 50th character...
String key = "Name =";
String space = "";
for(int i; i<(50-key.length); i++)
{space = space + " ";}
String value = "Bob\n";
System.out.println(key+space+value);
Put all of that in a loop and initialize/set the "key" and "value" variables before each iteration and you're golden. I would also use the StringBuilder class too which is more efficient.
#Override
public String toString() {
return String.format("%15s /n %15d /n %15s /n %15s", name, age, Occupation, status);
}
For decimal values you can use DecimalFormat
import java.text.*;
public class DecimalFormatDemo {
static public void customFormat(String pattern, double value ) {
DecimalFormat myFormatter = new DecimalFormat(pattern);
String output = myFormatter.format(value);
System.out.println(value + " " + pattern + " " + output);
}
static public void main(String[] args) {
customFormat("###,###.###", 123456.789);
customFormat("###.##", 123456.789);
customFormat("000000.000", 123.78);
customFormat("$###,###.###", 12345.67);
}
}
and output will be:
123456.789 ###,###.### 123,456.789
123456.789 ###.## 123456.79
123.78 000000.000 000123.780
12345.67 $###,###.### $12,345.67
For more details look here:
http://docs.oracle.com/javase/tutorial/java/data/numberformat.html
I'm writing something similar to smarterChild (that automated AOL Instant Messenger chat program from back in the day) and was curious about my method of determining sentence structure and response.
Currently, the bare bones design is to use 5 main methods which collectively determine the appropriate response given the user input:
1) Read(): initially accepts user input, then calls first of many methods: sentenceType()
2) getSentenceType(): User sentence type: Question, statement, directive, suggestion, etc…
3) getWho(): If question is about the computer, or about someone else…
4) getCategory(): What category the question is about (weather, sports…)
5) getResponse(): finally, form a response
These methods will query a database to determine if the user input contains key words...
Example of getSentenceType():
public String getSentenceType(String sentence) {
String type = null;
for (String s : db.getQuestions()) {
if (sentence.contains(s)) {
type = "question";
break;
}
else {
type = "statement";
}
}
return getWho(type, sentence);
}
Example of the final method which returns sentence structure:
//final call...
public String getResponse(String cat, String who, String type) {
String response = new String();
String auxVerb, subject, mainVerb, noun;
auxVerb = "do";
subject = who;
mainVerb = "like";
noun = cat;
//question structure: auxiliary verb + subject + main verb + noun/pronoun
// DO YOU LIKE MARY?
if (type.equals("question")) {
response = subject + " " + auxVerb + " " + mainVerb + " " + noun + ". ";
}
else {
response = auxVerb + " " + subject + " " + mainVerb + " " + noun + "?";
}
return response;
}
Sample database methods:
public String[] getQuestions() {
String[] questions = new String[] {"why", "?"};
return questions;
}
public String[] getWeather() {
String[] weather = new String[] {"cold", "hot", "rainy", "weather"};
return weather;
}
It will then progressively concatenate all the method results into a coherent response… then send that result back to Read(), which will print out the result to the user...
Is this an inefficient way to go about this? I know that if I continue down this path... to make a robust system, it will take tons of if else checks and a massive database to determine every possible type of response for user input.
Are there any suggested methods?
Thank you
I was wondering if someone can show me how to use the format method for Java Strings.
For instance If I want the width of all my output to be the same
For instance, Suppose I always want my output to be the same
Name = Bob
Age = 27
Occupation = Student
Status = Single
In this example, all the output are neatly formatted under each other; How would I accomplish this with the format method.
System.out.println(String.format("%-20s= %s" , "label", "content" ));
Where %s is a placeholder for you string.
The '-' makes the result left-justified.
20 is the width of the first string
The output looks like this:
label = content
As a reference I recommend Javadoc on formatter syntax
If you want a minimum of 4 characters, for instance,
System.out.println(String.format("%4d", 5));
// Results in " 5", minimum of 4 characters
To answer your updated question you can do
String[] lines = ("Name = Bob\n" +
"Age = 27\n" +
"Occupation = Student\n" +
"Status = Single").split("\n");
for (String line : lines) {
String[] parts = line.split(" = +");
System.out.printf("%-19s %s%n", parts[0] + " =", parts[1]);
}
prints
Name = Bob
Age = 27
Occupation = Student
Status = Single
EDIT: This is an extremely primitive answer but I can't delete it because it was accepted. See the answers below for a better solution though
Why not just generate a whitespace string dynamically to insert into the statement.
So if you want them all to start on the 50th character...
String key = "Name =";
String space = "";
for(int i; i<(50-key.length); i++)
{space = space + " ";}
String value = "Bob\n";
System.out.println(key+space+value);
Put all of that in a loop and initialize/set the "key" and "value" variables before each iteration and you're golden. I would also use the StringBuilder class too which is more efficient.
#Override
public String toString() {
return String.format("%15s /n %15d /n %15s /n %15s", name, age, Occupation, status);
}
For decimal values you can use DecimalFormat
import java.text.*;
public class DecimalFormatDemo {
static public void customFormat(String pattern, double value ) {
DecimalFormat myFormatter = new DecimalFormat(pattern);
String output = myFormatter.format(value);
System.out.println(value + " " + pattern + " " + output);
}
static public void main(String[] args) {
customFormat("###,###.###", 123456.789);
customFormat("###.##", 123456.789);
customFormat("000000.000", 123.78);
customFormat("$###,###.###", 12345.67);
}
}
and output will be:
123456.789 ###,###.### 123,456.789
123456.789 ###.## 123456.79
123.78 000000.000 000123.780
12345.67 $###,###.### $12,345.67
For more details look here:
http://docs.oracle.com/javase/tutorial/java/data/numberformat.html