I have a properties (cant change this file) and it looks like:
aaa.bbb.ccc.first=my first value
aaa.bbb.ccc.second=my second value
aaa.bbb.ccc.third=my third value
If I need any value in java classes I use i18n.getText("aaa.bbb.ccc.first") but it works only for single value.
Problem is because I dont know:
-value's names
-how many values are in aaa.bbb.ccc.~
How is it possible to get list of value aaa.bbb.ccc~?
You could use a MapFilter. Use the MapFilter(Properties p, String prefix) constructor.
public void test() {
Properties props = new Properties();
props.put("aaa.bbb.ccc.first", "my first value");
props.put("aaa.bbb.ccc.second", "my second value");
props.put("aaa.bbb.ccc.third", "my third value");
props.put("Other.props", "others");
MapFilter<String> filtered = new MapFilter(props, "aaa.bbb.ccc.");
for (Map.Entry<String, String> e : filtered.entrySet()) {
System.out.println("Key: " + e.getKey() + " Value: " + e.getValue());
}
System.out.println(filtered);
}
Hash maps are not meant for the kind of lookup you want to do: Tries and radix trees are. There is an implementation of the Patricia trie data structure (i.e., binary radix trees) in Apache Commons Collections: Just create a trie from your Map<String, Whatever> (you have a nice constructor at the purpose) and with prefixMap("aaa.bbb.ccc") you obtain the submap of all the entries whose key have that prefix.
Properties has a method propertyNames(). You can use that to get all the keys then do whatever you want from there.
Related
I would like to edit a yaml in java , ( I'm editing BungeeCord config file to create a system that launches instance of bungeecord with a defined by user port ) but in a precise way , i need to write exactly this in the yaml file :
listeners:
- query_port: 25577
motd: '&1Another Bungee server'
tab_list: GLOBAL_PING
query_enabled: false
proxy_protocol: false
ping_passthrough: false
priorities:
- lobby
bind_local_address: true
host: 0.0.0.0:25577
max_players: 1
tab_size: 60
force_default_server: false
I did something very similar but there is a vertical bar that prevents BungeeCord from reading the file :
public class ProxyYaml {
HashMap<String, Object> entries = new HashMap<String, Object>();
public ProxyYaml() {
entries.put("query_port", 25577);
entries.put("motd", "Hey Guys");
entries.put("tab_list", "GLOBAL_PING");
entries.put("query_enabled", false);
entries.put("proxy_protocol", false);
entries.put("ping_passthrough", false);
entries.put("priorities", Arrays.asList("lobby"));
entries.put("bind_local_address", true);
entries.put("host", "0.0.0.0:25577");
entries.put("max_players", 1);
entries.put("tab_size", 60);
entries.put("force_default_server", false);
}
public ArrayList<String> getProperties() {
ArrayList<String> finalString = new ArrayList<String>();
for(String entry : entries.keySet()) {
finalString.add(entry + ": " + entries.get(entry).toString());
}
return finalString;
}
}
( I'm using SimpleYaml api but I can change the api if needed )
File propsProxyFile = new File(path + "config.yml");
YamlFile propsProxyYaml = new YamlFile(propsProxyFile);
try {
propsProxyYaml.load(propsProxyFile);
propsProxyYaml.set("listeners", Arrays.asList(new ProxyYaml().getProperties()));
propsProxyYaml.save(propsProxyFile);
} catch (IOException | InvalidConfigurationException e) {
System.out.println(MainNetwork.logo + "Can't load proxy properties file");
return;
}
There is the code output ( with the vertical bar ) :
listeners:
- |
query_port: 25577
motd: '&1Another Bungee server'
tab_list: GLOBAL_PING
query_enabled: false
proxy_protocol: false
ping_passthrough: false
priorities:
- lobby
bind_local_address: true
host: 0.0.0.0:25577
max_players: 1
tab_size: 60
force_default_server: false
What should I do please ?
The pipe character (|) starts a YAML block scalar. It means that all the following lines are a literal string and not subject to further YAML parsing.
There are lots of strange things happening in your code, let's go over them:
public ArrayList<String> getProperties() {
ArrayList<String> finalString = new ArrayList<String>();
for(String entry : entries.keySet()) {
finalString.add(entry + ": " + entries.get(entry).toString());
}
return finalString;
}
You are manually transforming a mapping into a list of strings here. Why do you do that? You expect the final YAML file to contain the key-value pairs as mapping, so you should not transform them into a list of strings.
Let's discuss what happens here with a quick example:
Assume we have this java Map:
Map<String, String> value = Map.of("foo", "bar");
If we directly serialize this to YAML, we would get
foo: bar
but if we pipe it through your method, we'll get
- "foo: bar"
i.e. a sequence of one scalar value – because we manually transformed the mapping entry into a string! This is not what you want.
propsProxyYaml.set("listeners", Arrays.asList(new ProxyYaml().getProperties()));
You call Arrays.asList on the return value of getProperties() which is of type ArrayList<String> so asList will return a value of type List<ArrayList<String>>, which has a single entry that is the list you built. It is unclear why you call Arrays.asList unless you want to have a list of lists. According to the desired YAML output, this is not what you want.
Now let's discuss what the set method does. I don't really know SimpleYAML and frankly, its documentation is horrible as it basically only consists of the autogenerated API docs. The first parameter of the method is named path, which implies that it is not a simple mapping key.
What apparently happens is that the List<ArrayList<String>> value is transformed into a YAML sequence with one scalar value, and that scalar value contains all the string values you produced, separated by newlines. Without proper documentation, it is impossible to say whether this is expected behavior or a bug. In any case, it makes no sense.
Now the actual YAML contains a mapping at its root with one entry, whose key is the scalar listeners. Its value is a list that contains another mapping. This means, the type you actually want to serialize is
Map<String, List<Map<String, Object>>>
I suggest that you simply build a value of this type and use the SnakeYAML API, which does have proper documentation on how its serialization system works. SimpleYAML uses SnakeYAML under the hood anyway, and there seems to be no reason to use a poorly documented API with surprising behavior instead of a well-documented one.
You can also create a custom Java class instead of using Maps. Then the keys would become class fields.
I am trying to unit test a function that takes a HashMap and concatenates the keys into a comma separated string. The problem is that when I iterate through the HashMap using entrySet (or keySet or valueSet) the values are not in the order I .put() them in. IE:
testData = new HashMap<String, String>(0);
testData.put("colA", "valA");
testData.put("colB", "valB");
testData.put("colC", "valC");
for (Map.Entry<String, String> entry : testData.entrySet()) {
System.out.println("TestMapping " + entry.getKey());
}
Gives me the following output:
TestMapping colB
TestMapping colC
TestMapping colA
The string created by the SUT is ColB,ColC,ColA
How can I unit test this, since keySet(), valueSet(), etc are somewhat arbitrary in their order?
This is the function I am trying to test:
public String getColumns() {
String str = "";
for (String key : data.keySet()) {
str += ", " + key;
}
return str.substring(1);
}
There is no point in iterating over the HashMap in this case. The only reason to iterate over it would be to construct the expected String, in other words, perform the same operation as the method under test, so if you made an error implementing the method, you are likely to repeat the error when implementing the same for the unit test, failing to spot the error.
You should focus on the validity of the output. One way to test it, is to split it into the keys and check whether they match the keys of the source map:
testData = new HashMap<>();
testData.put("colA", "valA");
testData.put("colB", "valB");
testData.put("colC", "valC");
String result = getColumn();
assertEquals(testData.keySet(), new HashSet<>(Arrays.asList(result.split(", "))));
You are in control of the test data, so you can ensure that no ", " appears within the key strings.
Note that in its current form, your question’s method would fail, because the result String has an additional leading space. You have to decide whether it is intentional (in this case, you have to change the test to assertEquals(testData.keySet(), new HashSet<>(Arrays.asList(result.substring(1) .split(", "))));) or a spotted bug (then, you have to change the method’s last line to return str.substring(2);).
Don’t forget to make a testcase for an empty map…
HashMap does not maintain insertion order....If you want insertion order to be maintained use a linkedhashmap
Title says it all but for the sake of example.. I'm currently learning Java now and as a personal experiment I'm writing a soft representation of an ATM screen.
I'm attempting to store three hash maps inside of one value within a list of arrays. Thus, giving me the ability to add multiple strings, all containing their own set of three maps.
So - I want an Array List for "User" and within this list, will be three maps for "Checking" "Saving" and "CDs".
When adding a new User, this user is also given the ability to add/modify/remove three different "accounts."
Thoughts?
See if it could help you solve the problem
HashMap <String,HashMap> holder = new HashMap<>();
HashMap <Integer,Integer> hm1 = new HashMap<>();
HashMap <Integer,String> hm2 = new HashMap<>();
hm1.put(0,10000000);
hm1.put(1,5000);
hm2.put(0,"xxxx-xxxx-xxxx");
hm2.put(1,"yyyy-yyyy-yyyy");
holder.put("Amt", hm1);
holder.put("Info", hm2);
Iterator<String> iterator = holder.keySet().iterator();
while(iterator.hasNext())
{String key = iterator.next();
System.out.println("key: " + key + " value: " + holder.get(key));
}
Output:
key: Amt value: {0=10000000, 1=5000}
key: Info value: {0=xxxx-xxxx-xxxx, 1=yyyy-yyyy-yyyy}
you can 1st fetch the desired field like Amt using the key1 and then use the key2 (0,1,2....)
for each person to fetch corresponding value.
If I have the below property in my property file, how do I get the property key? I know how to get the value, but I am not sure how to get the key?
String myCountry="Australia";
I have this property along with others in my file. How do I get the key name (myCountry)? I would like to display it in System.out, so I need it, but I can't figure out how to get the key name, please help.
The point of a properties file is to associate configurable values with well-known keys. You should know the key and use it to find which configurable value has been associated with the key. Otherwise, a Properties object is a Map, and you can iterate through all the entries:
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
System.out.prinln(entry.getKey() + " = " + entry.getValue());
}
There is a method public Set<String> stringPropertyNames() in class java.util.Properties which is documented as:
Returns a set of keys in this property list where the key and its corresponding value are strings, including distinct keys in the default property list if a key of the same name has not already been found from the main properties list. Properties whose key or value is not of type String are omitted.
The returned set is not backed by the Properties object. Changes to this Properties are not reflected in the set, or vice versa.
Returns:
a set of keys in this property list where the key and its corresponding value are strings, including the keys in the default property list.
Why are you writing String myCountry="Australia"; in your properties file ? If you can simply keep like myCountry=Australia. And also no need to to write String .
All properties file values are String.
The java.util.Properties Class extends the java.util.HashTable Class, so has a keys() method. you can use this to retrieve a list of all the keys used in the file.
To printout a key for a given value something like this will work:
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
if(entry.getValue().equals("Australia")) {
System.out.prinln(entry.getKey() + " = " + entry.getValue());
}
}
This iterates through all the properties as key, value pairs until the pair with the correct value is found.
Also this might help:
import java.io.FileInputStream;
import java.util.Enumeration;
import java.util.Properties;
public class MainClass {
public static void main(String args[]) throws Exception {
Properties p = new Properties();
p.load(new FileInputStream("test.txt"));
Enumeration e = p.propertyNames();
for (; e.hasMoreElements();) {
System.out.println(e.nextElement());
}
}
}
Source: http://www.java2s.com/Tutorial/Java/0140__Collections/GettingakeyListfromProperties.htm
I am using file properties file and loop through that file.
Properties Config= new Properties();
Config.load(new FileInputStream("c:\\Config.properties"));
Enumeration e = Config.propertyNames();
while (e.hasMoreElements())
{ String key = (String) e.nextElement();
System.out.println(key + " -- " + Config.getProperty(key));
}
My Input in C:\Config.properties is
Input
fun1= abc
fun2=pqr
fun3 mno
Output
fun2 -- pqr
fun1 -- abc
fun3 -- mno
How can i get this what exactly in the Config file.
That's not possible using java.util.Properties as it extends java.util.Hashtable which is by nature unordered.
Your best bet is reading and parsing it yourself using for example a BufferedReader or a Scanner.
I however wonder why ordering is that important. There may be alternatives which I can only suggest if you elaborate the functional requirement behind this all in detail. Based on the given example, the closest what I can suggest you is the following:
List<String> funs = new ArrayList<String>();
for (int i = 1; i < Integer.MAX_VALUE; i++) {
String fun = config.getProperty("fun" + i);
if (fun == null) break;
funs.add(fun);
}
// ...
Properties is not an ordered map so you might want to create a SortedMap from the loaded properties. This guarantees that the keys are naturally ordered. But i assume you want the order to be the one that you specify in the properties file. There is no way to do it automatically. You need to code it yourself.
As BalusC suggested read line by line from beginning using file API, create a Pair/Tuple object and insert it in the List object or add it to SortedMap based upon your usage requirement
I would be shocked if there isn't a library that already does this. Try Apache Configuration, for example.