Merging two Config.yml Files in Java? - java

While programming my bukkit plugin, i realized that i needed to have my own config file so i can add comments into the file. I also needed to be able to update the config (if it has been created and is old).
I had also recently finished a simple jQuery plugin, where I used jQuery.extend and i merged two settings arrays. I wanted to know if this was possible in java with config files.
My Question:
Is there a way i can merge the new default config with the one the user already has? (Removing non-needed values or changing the names of the same strings)
An Explanation of the question:
Here is an example config.yml i might have:
# Comment here....
myString: defaultString
myBool: false
myList:
- Value
- Value 2
Pretty simple. Lets say this is my default config. The plugin has copied this config (if it is not already there) inside the plugin folder. But, this brings up one issue:
What if i need to update my config? (Add/Remove a bool, string, etc.)
One day, i say "I no longer need that boolean myBool". I remove it from the default config.yml and the config looks something like this:
# Comment here....
myString: defaultString
myList:
- Value
- Value 2
Or, i might need to add an extra string myNewString:
# Comment here....
myString: defaultString
myNewString: string
myList:
- Value
- Value 2
If i rewrite the config yml to my new "Default" config file, i will lose all the user's configuration settings.
Is there a way i can merge the new default config with the one the user already has and just add the new string with the default values?

If you are using Spring then you can make use of YamlPropertiesFactoryBean. It has built in support for reading multiple yaml files and merging them together. So that way you can obtain merged Map<String,Object> from your yaml files. Then if you wish you can make use of ObjectMapper to convert it to particular type.
eg.
YamlMapFactoryBean factory = new YamlMapFactoryBean();
factory.setResolutionMethod(ResolutionMethod.OVERRIDE_AND_IGNORE);
factory.setResources(...resources);
Map<String, Object> yamlValueMap = factory.getObject();
If multiple resources are provided the later ones will override entries in
the earlier ones hierarchically; that is, all entries with the same nested key
of type {#code Map} at any depth are merged. For example:
<pre class="code">
foo:
bar:
one: two
three: four
</pre>
plus (later in the list)
<pre class="code">
foo:
bar:
one: 2
five: six
</pre>
results in an effective input of
<pre class="code">
foo:
bar:
one: 2
three: four
five: six
</pre>
Note that the value of "foo" in the first document is not simply replaced
with the value in the second, but its nested values are merged.

Bukkit has a built in YamlConfiguration class with methods which allow you to get a value or specify a default value to retrieve if none exist, such as getString(String path, String default).
Gets the requested String by path, returning a default value if not found.
If the String does not exist then the specified default value will returned regardless of if a default has been identified in the root Configuration.
However it does not have a way to remove values from the configuration. If the new configuration is so different from the previous one, you might consider creating the new configuration, deleting the old and renaming the new to take it's place. Though I wouldn't be too concerned about it unless it's significantly different.

Related

Spring Boot - Apache Commons Configuration - How to get properties with the same suffix

I have an application.properties file which contains some properties like so:
foo.name=Some name
foo.link=https://www.example.com
foo.traits=a trait,another one,and another
...
bar.name=Another name
bar.link=https://www.anotherexample.com
bar.traits=some more traits,and some more,this is getting old
How can I get properties which share a similar suffix? For example, I want to stuff foo.name and bar.name into a list/array; similarly with the other properties. I've seen this done with properties that share a prefix, but never a suffix. Is regex the best approach? Or does this library have some function I haven't been able to find?
A note: this needs to be done dynamically; so, if someone were to add another property, say blah.name, it would be stuffed into the already existing array which already contains foo and bar.name.
Thanks in advance.
I don't think you can group property key/value pairs by the key's "suffix" out of the box. I think you need to implement a customization of a PropertyResolver or similar. Where you would scan all the available properties, pick the ones with the suffix of interest, load the interesting key/value pairs into a Map, and then inject a new dynamic custom Map property back into the Environment
Yes, a regex would be the route I'd take to identify candidate properties. Something like ^(.*\.)(.*)=(.*)$ where group 1 is the property key prefix, group 2 is the suffix of interest, and group 3 is the property value.

Using Groovy for variable expansion in Java properties

I frequently use standard Java property files for configuring my Groovy applications. One feature I have been missing is the ability to use variables as part of the property value so they can be expand dynamically during use. I thought I could provide this functionality using the following design:
Use a special format to annotate the properties that should be expanded. I have chosen to enclose such templates in double exclamation marks (!!). These property values are essentially a template to be expanded with the local variables
Before using the properties in the application, use the groovy 'evaluate' method to expand application variables in the template
Re-assign the original property key to the new value before use
So, if I have a property file config.properties with properties like:
version=2.3
local_lib=!!${env['GROOVY_HOME']}/${configProps.getProperty('version')}/lib!!
The local_lib property will be expanded from the GROOVY_HOME environment variable and the version property value.
In my application, I have coded this as follows:
//Load the environment variables and configuration file
env=System.getenv()
configFile=new File('config.properties')
configProps= new Properties()
configProps.load(configFile.newDataInputStream())
//Replace configuration property values with their expanded equivalent
configProps.each{
//if a property value is a template we evaluate it
if (it.value.startsWith('!!')){
valTemplate=it.value.replace('!!','"')
it.value=evaluate(valTemplate)
}
}
//then we use the expanded property values
This seems to work. When I do
println configProps
I see that the value is expanded and not null
However, the getProperty method for the expanded property returns null.
assert configProps.getProperty('local_lib')=='C:\\DEVTOOLS\\groovy-2.4.7/2.3/lib'
| | |
| null false
[local_lib:C:\DEVTOOLS\groovy-2.4.7/2.3/lib, version:2.3]
What is causing this discrepancy? I would have expected to return the value shown in the property map.
Your local_lib value looks like a String, but it isn't. It is a GString, only lazily coerced to String as needed (like when printing out the configProps map value).
Thus, a little known effect of Properties.getProperty() takes effect here. When the actual map value is not a String, Properties.getProperty() returns null.
So, in order to get the desired behavior, you need to coerce the GString to String before you store the value in the property map. Like so:
it.value=evaluate(valTemplate).toString()
or
it.value=evaluate(valTemplate) as String
Then you should see the desired results downstream.

java-Share data among different scenarios in a single feature

Cucumber java
My feature file looks like
Feature
Scenario1:.... Generate a unique number
Scenario2:.... Do some validations on the unique number generated
Using spring for dependency injection, the unique number generated # Scenario1 is assigned to a String, the same need to be used across the Scenario2 as well.
But I'm getting a String value as null #Scenario2. I think the dependency injection # scenario2 is creatin a new object and is getting the default value as null.
Please help me on to resolve this issue. Need to know how java objects can be passed across different scenarios in a single feature..
Use Singleton?
1) Generate unique number at 1st scenario
2) getInstance() at 2nd
Use gherkin with qaf where it provides different ways to share information between steps or scenarios.
For example, If your step returns value you can use like:
Then get text of 'element'
And store into 'applicaiton.refID'
to refer any stored value or any property you can use ${property}. For example
Given application to update is '${applicaiton.refID}'
You can applicaiton.refID in any of subsequent scenario.
If you want to do this in java step, you can write code something like below:
//store value for further use
getBundle().setProperty("applicaiton.refID","myvalue");
//retrieve applicaiton.refID any where
getBundle().getString("applicaiton.refID");

Add existing key as value to a new key in a java property file

I am using a properties file in my java project for storing the path of various resources.
Ex :- Here is my properties file :-
MACHINE_NAME = "//pranay"
Json_path1 = MACHINE_NAME//Json//j1.txt
Json_path2 = MACHINE_NAME//Json//j2.txt
The value of key MACHINE_NAME is not getting replaced with its value i.e pranay in another keys such as Json_path1 and Json_path2. Therefore i am unable to get the correct path.
How to give the key MACHINE_NAME so that its value gets replaced in the other key values.
You can't do this automatically - it's simply not a feature of Java property files. You'll need to write code to do this wherever you plan to load/use the properties file.
You should think about:
whether you want to make this more explicit, e.g. using ${MACHINE_NAME} instead of just MACHINE_NAME
whether you have a fixed list of replacements you want to support, or whether you want to pick up anything that looks like it's a replacement (much easier if you have a syntax such as ${...} of course)
whether replacements can be recursive, e.g. MACHINE_NAME = ${USER_NAME}-laptop - and how to handle cycles if so
Standard Properties mechanism cannot handle this, but there are extensions. Try to look at eproperties. In other case do the substitutions yourself.

Updating values of functional data properties doesn't remove old values, only adds new triples

I have an RDF Ontology with a functional property hasTrendValue which relates instances of a class with integer values. I want to change these values programmatically using Jena. I tried the following code:
Property hasTrend = ontModel.getDatatypeProperty(preFix+"hasTrendValue");
Individual regionQualifier = ontModel.getIndividual(activityName);
ontModel.addLiteral(regionQualifier,hasTrend,34);
PrintStream p = new PrintStream(ontoPath);
ontModel.write(p,null);
p.close();
This code executes correctly but, it does not update the already hasTrendValue value in the RDF; instead it adds a new hasTrendValue to the RDF ontology even though it declared as a functional property. What is the better way of doing this?
RDF does not have the concept of "change", only "add" and "remove". To change a value, you need to remove the old one and add the new one.
Declaring it as a functional property does not change this. Jena does not check the ontology on every operation. In fact, a functional property says that the object identifies one thing - it may be written in many ways. 001 and 1 are the same value. There may be multiple triples, it's not automatically wrong.

Categories

Resources