SPEL - Split and trim, de-limited values - java

I am trying to split String values based on de-limiter and trim them before putting them in a list.
I am able to split the values, could you please suggest how can be trim the list.
#Value("#{'${string-values}'.split(',')}")
private List<String> textList;
The problem it seems is, Split returns a list and I need to invoke trim() before storing them in the variable.

Check
Java - Split and trim in one shot
#Value("#{'${string-values}'.split('\\s*,\\s*')}")
private List<String> textList;

Better to provide no space between values in properties file.
To put a check in code it can be done in this way.
private List<String> textList;
public YourController(#Value("${string-values}") String propertyFromFile) {
this.textList = new ArrayList<>();
Arrays.asList(propertyFromFile.split(",")).forEach(b-> this.textList.add(b.trim()));
}

I think it may be better to use #Configuration and then process that instead of doing like this, however you can add a new annotation on top of value annotation and use that annotation to process the list. For example
#Target(value = {ElementType.TYPE})
#Value
public #interface Trim {
//Override the method you want to override
}
Public TrimPricessor {
//Implement the annotation method here
}

Since you are using Spring Boot, use:
#Component
#ConfigurationProperties(prefix = "foo.bar")
public class MyConfig {
private List<String> list = new ArrayList<>();
public List<String> getList() {
return this.list;
}
public void setList(List<String> list) {
this.list = list;
}
}
foo.bar.list= a , b, c
The list entries are trimmed.

Related

List<String> Modification using Reflection

I have List like below code snippet:
List<String> nameList = new ArrayList<>();
nameList.add("Robert");
nameList.add("Tom");
nameList.add("Curran");
//robert, tom, curran
Now I want to modify these list values using reflection API in Java. Output something like below if we print list:
//xxx,xxx,xxx
You can achieve this using Java reflection as follows:
The class that contains nameList:
public class SampleClass {
List<String> nameList;
}
This is a simple test method using SampleClass:
#Test
public void testReflection() throws NoSuchFieldException, IllegalAccessException {
SampleClass sample= new SampleClass();
sample.nameList = new ArrayList<>();
sample.nameList.add("Robert");
sample.nameList.add("Tom");
sample.nameList.add("Curran");
Field fieldList = SampleClass.class.getDeclaredField("nameList");
fieldList.setAccessible(true);
List<String> listToModify = (List<String>) fieldList.get(sample);
for (int i =0;i<listToModify.size();i++) {
if(listToModify.get(i).equals("Robert"))
listToModify.set(i, "xxxxxx");
};
System.out.println(sample.nameList.get(0));
System.out.println(sample.nameList.get(1));
System.out.println(sample.nameList.get(2));
}
Output:
xxxxxx
Tom
Curran
NOTE: I don't recommend using setAccessible(true), it destroys OOP encapsulation.
You can add the custom annotation concept to solve this problem. By creating custom annotation you can simply modify those fields only whichever is required. Fields that need to be modified will be annotated with custom annotation and the rest are ignored. For reference: Java reflection.

how to sort application properties value

Is it possible to sort a List interface with object of ArrayList()?
List</*classname*/> list = new ArrayList<>();
I'm using application.properties of Spring boot to set the value in List interface
ModuleNames.java
public class ModuleNames {
private String moduleId;
private String moduleName;
//getters & setters.
}
Controller
#RequestMapping(value = "/getModuleNames", method = { RequestMethod.GET })
public #ResponseBody List<String> getModuleNames() {
List<ModuleNames> moduleNamesList = new ArrayList<>();
moduleNamesList.add(new ModuleNames("Country", env.getProperty("bulkUpload.location.country")));
moduleNamesList.add(new ModuleNames("State", env.getProperty("bulkUpload.location.state")));
moduleNamesList.add(new ModuleNames("City", env.getProperty("bulkUpload.location.city")));
moduleNamesList.add(new ModuleNames("Area Pincode", env.getProperty("bulkUpload.location.areaPincode")));
return moduleNamesList;
does anybody know how to sort the list.
I've tried
moduleNamesList.sort(Comparator.naturalOrder());
Collections.sort(moduleNamesList);
but both doesn't seem to work. Please help me here
You need to write your custom comparator for ModuleNames object, because Java doesn't know how to sort it as it is not a primitive type.
For example:
Collections.sort(moduleNamesList, new Comparator<ModuleNames>() {
#Override
public int compare(ModuleNames o1, ModuleNames o2) {
return o1.getModuleId().compareTo(o2.getModuleId());
}
});
or
moduleNamesList.sort(new Comparator<ModuleNames>() {
#Override
public int compare(ModuleNames o1, ModuleNames o2) {
return o1.getModuleName().compareTo(o2.getModuleName());
}
});
You can avoid defining the comparison logic by using an instance method reference and the Comparator.comparing method – which extracts and creates a Comparable based on that function.
We're going to use the getters to build the Lambda expression.
sort the list by moduleName:
moduleNamesList.sort(Comparator.comparing(ModuleNames::getModuleName));
sort the list by moduleName in reverse order:
moduleNamesList.sort(Comparator.comparing(ModuleNames::getModuleName).reversed());
sort the list by first moduleName and then moduleId:
moduleNamesList.sort(Comparator.comparing(ModuleNames::getModuleName).thenComparing(ModuleNames::getModuleId));
You can also use custom comparator:Use This Reference
moduleNamesList.sort(Comparator.comparing(ModuleNames::getName));
moduleNamesList.sort(Comparator.comparing(mn -> mn.name));
Or even more sorted:
moduleNamesList.sort(Comparator.comparing(ModuleNames::getName)
.thenComparing(ModuleNames::getId));

Extract a List of values for keys with the same prefix using #JsonPropperty

For this JSON:
{"key.a": "a", "key.b": "b"}
I want to load values "a" and "b" to List using #JsonProperty and Jackson. I tried:
#JsonProperty("key.*")
List<String> values;
But it doesn't work. Any thoughts?
You can use the JsonAnySetter annotation, which will map any unknown Json property, on a setter method:
private List<String> values = new ArrayList<>();
#JsonAnySetter
public void setValues(String key, String value) {
// You can perform a pattern validation on the key if wanted
this.values.add(value);
}
public List<String> getValues() {
return this.values;
}
An alternative solution would be to implement a custom JsonSerializer & JsonDeSerializer as well.

Mockito; verify method was called with list, ignore order of elements in list

I have a class (ClassA) that get the files in a directory. It scans the given directory for files matching a regex. For each matching file, it adds a File Object to a list.
Once the directory is processed, it passes the List of Files to another Class (ClassB) for processing
I am writing unit tests for ClassA, so am mocking ClassB using Mockito, and injecting it into ClassA.
I then want to verify in different scenarios the contents of the list that is passed to ClassB (ie my mock)
I've stripped back the code to the following
public class ClassA implements Runnable {
private final ClassB classB;
public ClassA(final ClassB classB) {
this.classB = classB;
}
public List<File> getFilesFromDirectories() {
final List<File> newFileList = new ArrayList<File>();
// ...
return newFileList;
}
public void run() {
final List<File> fileList = getFilesFromDirectories();
if (fileList.isEmpty()) {
//Log Message
} else {
classB.sendEvent(fileList);
}
}
}
The test class looks like this
#RunWith(MockitoJUnitRunner.class)
public class AppTest {
#Rule
public TemporaryFolder folder = new TemporaryFolder();
#Mock
private ClassB mockClassB;
private File testFileOne;
private File testFileTwo;
private File testFileThree;
#Before
public void setup() throws IOException {
testFileOne = folder.newFile("testFileA.txt");
testFileTwo = folder.newFile("testFileB.txt");
testFileThree = folder.newFile("testFileC.txt");
}
#Test
public void run_secondFileCollectorRun_shouldNotProcessSameFilesAgainBecauseofDotLastFile() throws Exception {
final ClassA objUndertest = new ClassA(mockClassB);
final List<File> expectedFileList = createSortedExpectedFileList(testFileOne, testFileTwo, testFileThree);
objUndertest.run();
verify(mockClassB).sendEvent(expectedFileList);
}
private List<File> createSortedExpectedFileList(final File... files) {
final List<File> expectedFileList = new ArrayList<File>();
for (final File file : files) {
expectedFileList.add(file);
}
Collections.sort(expectedFileList);
return expectedFileList;
}
}
The problem is that this test works perfectly fine on windows, but fails on Linux. The reason being that on windows, the order that ClassA list the files matches the expectedList, so the line
verify(mockClassB).sendEvent(expectedFileList);
is causing the problem expecetdFileList = {FileA, FileB, FileC} on Windows, whereas on Linux it will be {FileC, FileB, FileA}, so the verify fails.
The question is, how do I get around this in Mockito. Is there any way of saying, I expect this method to be be called with this parameter, but I don't care about the order of the contents of the list.
I do have a solution, I just don't like it, I would rather have a cleaner, easier to read solution.
I can use an ArgumentCaptor to get the actual value passed into the mock, then can sort it, and compare it to my expected values.
final ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
verify(mockClassB).method(argument.capture());
Collections.sort(expected);
final List<String> value = argument.getValue();
Collections.sort(value);
assertEquals(expecetdFileList, value);
As noted in another answer, if you don't care about the order, you might do best to change the interface so it doesn't care about the order.
If order matters in the code but not in a specific test, you can use the ArgumentCaptor as you did. It clutters the code a bit.
If this is something you might do in multiple tests, you might do better to use appropriate Mockito Matchers or Hamcrest Matchers, or roll your own (if you don't find one that fills the need). A hamcrest matcher might be best as it can be used in other contexts besides mockito.
For this example you could create a hamcrest matcher as follows:
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class MyMatchers {
public static <T> Matcher<List<T>> sameAsSet(final List<T> expectedList) {
return new BaseMatcher<List<T>>(){
#Override
public boolean matches(Object o) {
List<T> actualList = Collections.EMPTY_LIST;
try {
actualList = (List<T>) o;
}
catch (ClassCastException e) {
return false;
}
Set<T> expectedSet = new HashSet<T>(expectedList);
Set<T> actualSet = new HashSet<T>(actualList);
return actualSet.equals(expectedSet);
}
#Override
public void describeTo(Description description) {
description.appendText("should contain all and only elements of ").appendValue(expectedList);
}
};
}
}
And then the verify code becomes:
verify(mockClassB).sendEvent(argThat(MyMatchers.sameAsSet(expectedFileList)));
If you instead created a mockito matcher, you wouldn't need the argThat, which basically wraps a hamcrest matcher in a mockito matcher.
This moves the logic of sorting or converting to set out of your test and makes it reusable.
An ArgumentCaptor probably is the best way to do what you want.
However, it seems that you don’t actually care about the order of the files in the List. Therefore, have you considered changing ClassB so that it takes an unordered collection (like a Set) instead?
A one-liner using argThat which compares the two lists as sets:
verify(mock).method(argThat(list -> new HashSet<>(expected).equals(new HashSet<>(list))));
You can use an ArgumentCaptor and then Hamcrest's Matchers.containsInAnyOrder() for assertion like this:
ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
verify(mockClassB).method(argument.capture());
List<String> value = argument.getValue();
assertThat(value, containsInAnyOrder("expected", "values");

Using Jackson XML binding to map lists to Pojo

I am using Jackson's XML binding to convert XML into a Java List, via an initial mapping to a Java POJO. What I have written works but I don't think I am using Jackson correctly.
I am stuck with this ugly XML:
<groups>
<groups>One</groups>
<groups>Two</groups>
<groups>Three</groups>
</groups>
Here is the Java POJO I am using. Note the setGroups(String) method is actually adding to the list.
public class Groups {
private List<String> groups = new ArrayList<String>();
public void setGroups(String group) {
groups.add(group);
}
public List<String> getGroups() {
return this.groups;
}
}
Here is how I invoke Jackson's XmlMapper.
public List<String> getListOfGroups(String xmlDoc) {
XmlMapper mapper = new XmlMapper();
Groups groups = mapper.readValue(xmlDoc, Groups.class);
return groups.getGroups();
}
This is actually working as I need it to work. I get a Groups class with a list populated with the elements I expect. I am wondering, is approach is correct? I don't like that I have a setter doing an add but everything I've tried has not worked.
Your POJO could be simple like this:
public class Groups {
private List<String> groups;
public List<String> getGroups() {
return this.groups;
}
}
It will work fine since you use the MapperFeature.USE_GETTERS_AS_SETTERS (enabled by default).

Categories