I am trying to use indexOf to find placeholders in a String ("${...}").
My small example below works fine so far, but obviously only for the first occurence. How could I change this code to be able to go through all the placeholders and rebuild the String in the end. The input String can be random and doesn't have a set number of placeholders in it. Not really sure where to go from here.
// example Hashmap
HashMap <String, String> placeHolderMap = new HashMap<String, String>();
placeHolderMap.put("name", "device");
placeHolderMap.put("status", "broken");
placeHolderMap.put("title", "smartphone");
// input String
String content = "This ${name} is ${status} and categorized as ${title} in the system";
int left = content.indexOf("${");
int right = content.indexOf("}");
// getting the name of the placeholder, if the placeholdermap contains the placeholder as a key it sets the placeholder to the corresponding value
String contentPlaceHolder = content.substring(left+2, right);
if (placeHolderMap.containsKey(contentPlaceHolder)){
contentPlaceHolder = placeHolderMap.get(contentPlaceHolder);
}
content = content.substring(0, left) + contentPlaceHolder + content.substring(right+1);
Currently, the output would be "This device is ${status} and categorized as ${title} in the system"
Why don't you use a String.replaceAll() method?
Map<String, String> placeHolderMap = new HashMap<>();
placeHolderMap.put("\\$\\{name}", "device");
placeHolderMap.put("\\$\\{status}", "broken");
placeHolderMap.put("\\$\\{title}", "smartphone");
// input String
String content = "This ${name} is ${status} and categorized as ${title} in the system";
for (Map.Entry<String, String> entry : placeHolderMap.entrySet()) {
content = content.replaceAll(entry.getKey(), entry.getValue());
}
Update Stefan, Neil and Kennet, thank you.
UPDATE 17/07/17
You can also use String.replace() method which does not use regex, or, alternatively, use Pattern.quote() method:
Map<String, String> placeHolderMap = new HashMap<>();
placeHolderMap.put("${name}", "device");
placeHolderMap.put("${status}", "broken");
placeHolderMap.put("${title}", "smartphone");
// input String
String content = "This ${name} is ${status} and categorized as ${title} in the system";
for (Map.Entry<String, String> entry : placeHolderMap.entrySet()) {
content = content.replace(entry.getKey(), entry.getValue());
// content = content.replaceAll(Pattern.quote(entry.getKey()), entry.getValue());
}
You need to invoke your method recusively:
private String replace(String content) {
int left = content.indexOf("${");
if (left < 0) {
// breaking the recursion
return content;
}
int right = content.indexOf("}");
// getting the name of the placeholder, if the placeholdermap contains the placeholder as a key it sets the placeholder to the corresponding value
String contentPlaceHolder = content.substring(left + 2, right);
if (placeHolderMap.containsKey(contentPlaceHolder)) {
contentPlaceHolder = placeHolderMap.get(contentPlaceHolder);
}
content = content.substring(0, left) + contentPlaceHolder + content.substring(right + 1);
return replace(content);
}
Related
Below I have a url:
https://test.com/login/?response_type=code&redirect_uri=https%3A%2F%2Ftest.com&client=testclient&successUrl=https%3A%2F%2Flocalhost%3A4444%2Ftestsite%2Foauth%2Ftestendpoint%3Fresponse_type%3Dcode%26redirect_uri%3Dhttps%253A%252F%252Ftest.com%26client%3Dtestclient
I decode it so that it looks like a much tidier url string:
public void testMethod() {
final String url = URLDecoder.decode(getLastResponse().getHeaders().getFirst("location"), "UTF-8");
}
Now what I want to do is break this url apart by domain and query params and assign them. This is so I can perform asserts against them. I head the way to do this is through value key pairs but I am struggling to grasp the concept of it.
This is my attempt below but it is in a mess. Does anybody know how I can use the LinkedHashMap to help perform the key value pairs? Also, as you see in the url, some params appear twice so will need help in checking against multiple params
final Map<String, List<String>> query_pairs = new LinkedHashMap<>();
final String[] pairs = url.split("&|\\?");
for (String pair : pairs) {
final int idx = pair.indexOf("=");
final String key = idx > 0 ? url : pair;
if (!query_pairs.containsKey(key)) {
query_pairs.put(key, new LinkedList<>());
}
final String value = idx > 0 && pair.length() > idx + 1 ? url : null;
query_pairs.get(key).add(value);
}
It would be easier to start with URL, and decode the parameter values as mentioned in the other references noted in the comments. Here is an example that uses Java streams to break the url:
public static Map<String, List<String>> split(URL url) {
return Arrays.stream(url.getQuery().split("&"))
.map(s -> s.split("="))
// filter out empty parameter names (as in Tomcat) "?&=&&=value&"
.filter(arr -> arr.length > 0 && arr[0].length() > 0)
//.peek(a -> System.out.println(Arrays.toString(a)))
.collect(Collectors.groupingBy(key -> URLDecoder.decode(key[0], StandardCharsets.UTF_8),
LinkedHashMap::new,
Collectors.mapping(value -> value.length < 2 ? "" : URLDecoder.decode(value[1], StandardCharsets.UTF_8), Collectors.toList())))
;
}
You may want to adjust the filter to ignore empty property fields (queries with &&) or give zero length param values as null or "". Example call:
String s = "http://stackoverflow.com/page?&source=somewhere&date=2020-10-01&flag3&name1=val%20ue1&date=2020-12-31&&=&flag&name1=secondvalue&arg=v";
URL url = new URL(s);
Map<String, List<String>> map = split(url);
System.out.println(" => "+map);
List<String> list = map.get("name1");
System.out.println("KEYS of name1 ="+list);
Prints:
{date=[2020-10-01, 2020-12-31], =[, ], flag=[], arg=[v], flag3=[], source=[somewhere], name1=[val ue1, secondvalue]}
KEYS of name1 =[val ue1, secondvalue]
I've created a hash map that groups unique keys that combine three parameters, i.e. customer, sc and admin. I want to create a unique list of keys with a list of servers attached. I've implemented the following:
public static void main(String[] args) {
String items = "customer1^sc1^admin1|server1~" +
"customer1^sc1^admin1|server2~" +
"customer1^sc1^admin1|server3~" +
"customer2^sc1^admin1|server1~" +
"customer3^sc1^admin1|server3~" +
"customer3^sc1^admin1|server2~";
// Set up raw data
List<String> splitItems = Arrays.asList(items.split("\\s*~\\s*"));
// Display raw data
System.out.println("Raw List: " + items);
// Create a hash map containing customer name as key and list of logs as value
HashMap<String, List<String>> customerHashMap = new HashMap<>();
// Loop through raw data
for (String item : splitItems) {
// Create new lists. One for customers and one for logs
// List<String> customerList = new ArrayList<>();
List<String> logList;
String list[] = item.split("\\|");
String customer = list[0];
String log = list[1];
logList = customerHashMap.get(customer);
if (logList == null){
logList = new ArrayList<>();
customerHashMap.put(customer, logList);
}
logList.add(log);
// System.out.println(logList);
}
// Print out of the final hash map. Customer "a" should only have "a" logs, customer "b" with "b", etc.
System.out.println("");
List<String> hashMapList = new ArrayList<String>();
Iterator it = customerHashMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pair = (Map.Entry) it.next();
String output = pair.getKey() + "|" + pair.getValue().toString();
hashMapList.add(output);
it.remove();
}
String hashMapResultString = hashMapList.toString();
String hashMapResultFormatted = hashMapResultString.replaceAll("[\\[\\]]", "");
System.out.println(hashMapResultFormatted);
}
Raw List: customer1^sc1^admin1|server1~customer1^sc1^admin1|server2~customer1^sc1^admin1|server3~customer2^sc1^admin1|server1~customer3^sc1^admin1|server3~customer3^sc1^admin1|server2~
Hash Map String:
customer2^sc1^admin1|server1, customer3^sc1^admin1|server3, server2, customer1^sc1^admin1|server1, server2, server3
I now want to use the hash map to create a string which will be parsed further (don't ask lol). So I set the keys and values of the hash map to a string which separates them with a unique delimiter |. The problem is that because the key is a List<String>, when printing I can't ascertain the beginning of every new key if its value is a list with more than one item, i.e. customer3^sc1^admin1|server3, server2, is followed immediately by customer1^sc1^admin1|server1, server2, server3. I need a delimiter here that separates them.
My ideal output would look like this:
customer2^sc1^admin1|server1~customer3^sc1^admin1|server3, server2~customer1^sc1^admin1|server1, server2, server3~...
How can I achieve this?
Update:
This is the answer I ultimately found useful for my particular problem:
StringBuilder s = new StringBuilder();
for (Map.Entry<String, List<String>> entry : customerHashMap.entrySet()) {
s.append(entry.getKey() + "|");
List<String> list = entry.getValue();
for (String item : list) {
if (item != list.get(list.size() - 1)) {
s.append(item + "^");
} else {
s.append(item);
}
}
s.append("~");
}
System.out.println(s.toString());
You can iterate through a map's entry set:
StringBuilder s = new StringBuilder();
for(Map.Entry<String,List<String>> entry : map.entrySet()) {
s.append(entry.getKey() + "\n");
List<String> list = entry.getValue();
for(String item : list) {
s.append(" " + item + "\n");
}
}
return s.toString();
For the sake of a clearer example, I've output a different format from the one you asked for, but this illustrates how to work with a map of list values. When adapting to your needs, have a look at java.util.StringJoiner and the related Collectors.joining(); it may well be useful.
Streams can be handy here:
String encoded = map.entrySet().stream()
.map( entry -> entry.getValue().stream()
.collect(Collectors.joining("^"))
+ "|" + entry.getKey())
.collect(Collectors.joining("~"));
What happens here is:
We get a stream of Entry<String,List<String> out of the map
The lambda entry -> ... converts each entry into a string of the form val1^v2^v3^...^valN|key, i.e. we are mapping a Stream<Entry<>> into a Stream<String>.
the final collect() joins the stream of strings into a single string using ~ as a delimiter.
I am using a file that consists of:
"word","wordtype","definition"
"Base","n.","The lower part of a robe or petticoat."
"Base","n.","An apron."
The output is as follows:
key: "base" value: ["word""wordtype""definition", "Base""n.""The lower part of a robe or petticoat.", "Base""n.""An apron."]
key: "word" value: ["word""wordtype""definition", "Base""n.""The lower part of a robe or petticoat.", "Base""n.""An apron."]
Desired outcome:
key: "base" value: [ "Base""n.""The lower part of a robe or petticoat.", "Base""n.""An apron."]
key: "word" value: ["word""wordtype""definition"]
Can someone point me in the right direction?
BufferedReader br = new BufferedReader( new InputStreamReader(new FileInputStream(file)));
String line = null;
TreeMap<String, List<String>> def = new TreeMap<String, List<String>>();
List<String> values = new ArrayList<String>();
try {
while ((line = br.readLine()) != null) {
String []parts =line.split(",");
String key = null;
for (int i = 0; i < parts.length; i++){
key = parts[0];
}
values.add(parts[0] + parts[1] + parts[2]);
def.put(key.toLowerCase(), values);
}
A Map cannot work as you request. Any key can only be mapped to a single value.
If you want something akin to what you're doing, but where you can have multiple values for a given key, you could do something like:
List<Map.Entry<String, List<String>>> def = new ArrayList<>();
Map.Entry<String, List<String>> entry = new AbstractMap.SimpleEntry<>(key, list);
def.add(entry);
Then iterate through your def:
for (Map.Entry<String, List<String>> entry : def) {
System.out.println(String.format("Key: %s. Values: %s",
entry.getKey(),
Arrays.toString(entry.getValue().toArray())));
}
Edit:
For your comment: If you want that, you can always roll your own type to store in the Map (or List if you still need duplicate keys):
class WordDescription {
final String wordType;
final String definition;
WordDescription(String wordType, String definition) {
this.wordType = wordType;
definition = definition;
}
String getWordType() {
return wordType;
}
String getDefinition() {
return definition;
}
}
And use that in a List<Map.Entry<String, WordDescription>>. You can make wordType an enum if there's a pre-defined set of them (adjective, noun, etc.).
I have string variable String temp="acc=101&name=test"; and now how to get the value of name param from temp string.
temp.split("&")[1].split("=")[1]
public static Map<String, String> getParamMap(String query)
{
String[] params = query.split("&");
Map<String, String> map = new HashMap<String, String>();
for (String param : params)
{
String name = param.split("=")[0];
String value = param.split("=")[1];
map.put(name, value);
}
return map;
}
String temp="acc=101&name=test";
Map<String, String> map = getParamMap(temp);
for(Object object :map.keySet()){
System.out.println("key= "+object +" value= "+map.get(object));
}
System.out.println(map.get("name"));
Here is a non-general way
String str = "name=";
System.out.println(temp.substring(temp.indexOf(str) + str.length()));
It could be implemented in more general way of course:
String temp = "acc=101&name=test";
StringTokenizer st = new StringTokenizer(temp, "&");
String paramName = "name";
String paramValue = "";
while(st.hasMoreElements()) {
String str = st.nextToken();
if (str.contains(paramName)) {
paramValue = str.substring(str.indexOf(paramName) + paramName.length() + 1);
break;
}
}
System.out.println(paramValue);
You can use a method like below
public static String getValue(String queyStr, String paraamName){
String[] queries=queyStr.split("&");
for(String param:queries){
if(param.indexOf(paraamName)!=-1)
return param.split("=")[1];
}
return null;
}
And call the method like
getValue(temp, "name")
public static void main(String[] args)
{
String temp = "acc=101&name=test";
System.out.println(temp.split("&")[1].split("=")[1]);
}
If you are looking for a way to parse GET-Parameters out of an URL:
public static Map<String, String> splitQuery(URL url) throws UnsupportedEncodingException {
Map<String, String> query_pairs = new LinkedHashMap<String, String>();
String query = url.getQuery();
String[] pairs = query.split("&");
for (String pair : pairs) {
int idx = pair.indexOf("=");
query_pairs.put(URLDecoder.decode(pair.substring(0, idx), "UTF-8"), URLDecoder.decode(pair.substring(idx + 1), "UTF-8"));
}
return query_pairs;
}
You can access the returned Map using <map>.get("name"), with the URL given in your question this would return "test".
Assuming you have constant format :
String temp="acc=101&name=test";
String result =temp.substring(temp.lastIndexOf("=")+1,temp.length());
//result is test
String temp="acc=101&name=test";
String[] split = temp.split("&");
String[] name = split[1].split("=");
System.out.println(name[1]);
I would put the whole parameter in a HashMap so it is easy to get the values.
HashMap<String, String> valuemap = new HashMap<String, String>();
If you do it like so, you have to split the values at the right place...
String temp="acc=101&name=test";
valuemap.put(temp.split("&")[0].split("=")[0], temp.split("&")[0].split("=")[1]);
valuemap.put(temp.split("&")[1].split("=")[0], temp.split("&")[1].split("=")[1]);
...and put them into your HashMap. Than you have a nice collection of all your values and it is also better if you have more than only that two values. If you want the value back, use:
valuemap.get("acc")
valuemap.get("name")
I would like to parse a string which is basically a URL. I need to check simply that a parameters is passed to it or not.
so http://a.b.c/?param=1 would return true http://a.b.c/?no=1 would return false and http://a.b.c/?a=1&b=2.....¶m=2 would return true since param is set
I am guessing that it would involve some sort of regular expression.
Java has a builtin library for handling urls: Spec for URL here.
You can create a URL object from your string and extract the query part:
URL url = new URL(myString);
String query = url.getQuery();
Then make a map of the keys and values:
Map params<string, string> = new HashMap<string, string>();
String[] strParams = query.split("&");
for (String param : strParams)
{
String name = param.split("=")[0];
String value = param.split("=")[1];
params.put(name, value);
}
Then check the param you want with params.containsKey(key);
There is probably a library out there that does all this for you though, so have a look around first.
String url = "http://a.b.c/?a=1&b=2.....¶m=2";
String key = "param";
if(url.contains("?" + key + "=") || url.contains("&" + key + "="))
return true;
else
return false;