I have students list that I get from database. I applied predicates to List and partition the List with valid, invalid students. Now for invalid students I want to generate Map with student as a Key and error message as a value. Because I need to generate report for each student.Here what I am doing but I don't know whether it is a good approach or not or is there a better way to do it.
Actually after getting invalid students list I am trying to create a function but i Think code is getting messy and may be there is a better approach to do it. Here what I am doing
private List<Predicate<OlccStudent>> getAllPredicates() {
List<Predicate<OlccStudent>> allPredicates = Arrays.asList(
isValidFirstName(),
isValidMiddleInitial(),
isValidLastName(),
isValidStreetAddress(),
isValidCity(),
isValidState(),
isValidZip(),
isValidDateOfBirth(),
isValidSsn(),
isValidTestDate(),
isValidTestAnswers(),
isValidProviderCode(),
isValidInstructorCode()
);
return allPredicates;
}
public Map<Boolean, List<OlccStudent>> getStudentsMap(List<OlccStudent> olccStudentsList) {
List<Predicate<OlccStudent>> allPredicates = getAllPredicates();
Predicate<OlccStudent> compositePredicate = allPredicates.stream()
.reduce(w -> true, Predicate::and);
Map<Boolean, List<OlccStudent>> studentsMap= olccStudentsList
.stream()
.collect(Collectors.partitioningBy(compositePredicate));
return studentsMap;
}
public Map<OlccStudent, String> getInvalidStudentsMap(Map<Boolean, List<OlccStudent>> studentsMap) throws Exception {
List<OlccStudent> invalidStudentsList =
studentsMap.entrySet()
.stream()
.filter(p -> p.getKey() == Boolean.FALSE)
.flatMap(p -> p.getValue().stream())
.collect(Collectors.toList());
Function<List<OlccStudent>, Map<OlccStudent, String>> invalidStudentFunction = list -> {
Map<OlccStudent, String> invalidStudentsMap = new LinkedHashMap<>();
list.forEach(student-> {
String errorMessage = getStudentErrorMessage(student);
invalidStudentsMap.put(student, errorMessage);
});
return invalidStudentsMap;
};
Map<OlccStudent, String> invalidStudentsMap = invalidStudentFunction.apply(invalidStudentsList);
return invalidStudentsMap;
return null;
}
private String getStudentErrorMessage(OlccStudent student) {
String firstName = student.getFirstName();
String middleInitial = student.getMiddleInitial();
String lastName = student.getLastName();
String streetAddress = student.getStreetAddress();
....
StringBuilder errorBuilder = new StringBuilder();
//The predicate 'negate' method returns a predicate that represents the logical negation or opposite
if (isValidFirstName().negate().test(student)) {
String error = "Invalid FirstName: " + firstName;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidMiddleInitial().negate().test(student)) {
String error = "Invalid Middle Initial: " + middleInitial;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidLastName().negate().test(student)) {
String error = "Invalid LastName: " + lastName;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidStreetAddress().negate().test(student)) {
String error = "Invalid StreetAddress: " + streetAddress;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
...
if (errorBuilder.length() > 0) {
errorBuilder.deleteCharAt(errorBuilder.length() - 1);
}
return errorBuilder.toString().trim();
}
Actually I am confuse with the getStudentErrorMessage()
that I am calling from list.forEach()
. I know collectors provide you Collectors.joining
function. Actually I want to do it in a Predicate manner. Like I created list of all predicates and then use it in the streams. Can I do similar thing with my error messages? Like I create stream from my invalid students List
, and using Collectors.toMap()
, put my Student as a Key and error message as its value.
Thanks
Edit ----------------
public class OlccStudentPredicate {
public static Predicate<OlccStudent> isValidTestDate() {
return p -> isValidDate(p.getTestDate());
}
public static Predicate<OlccStudent> isValidDateOfBirth() {
return p -> isValidDate(p.getDateOfBirth());
}
...
private static boolean isValidDate(String date) {
boolean valid = false;
if (StringUtils.isNotBlank(date)) {
try {
LocalDate.parse(date, DateTimeFormatter.ofPattern("MM/dd/yyyy"));
valid = true;
} catch (DateTimeParseException e) {
}
}
return valid;
}
@Test
public void test() {
List<OlccStudent> olccStudentsList = getOlccStudentsList();
try {
Map<OlccStudent, String> map = getInvalidStudentsMap(olccStudentsList);
System.out.println();
//olccStudentService.getStudentsMap(olccStudentsList);
} catch (Exception e) {
e.printStackTrace();
}
}
private List<OlccStudent> getOlccStudentsList() {
List<OlccStudent> olccStudentsList = new ArrayList<>();
OlccStudent student1 = new OlccStudent(1L, 1L, "firstName1", "middleInitial1", "lastName1", "email1",
"streetAdress1", "city1", "state1", "1234", "01/22/2015", "phoneNumber1", "01/22/2015",
"123456789", "testForm1", "providerCode1", "instructorCode1", "surveyAnswer1",
"testIdentifier1", "testAnswers1");
OlccStudent student2 = new OlccStudent(2L, 2L, "firstName2", "middleInitial2", "lastName2", "email2",
"streetAdress2", "city2", "state2", "5678", "02/22/2015", "phoneNumber2", "02/22/2015",
"987654321", "testForm2", "providerCode2", "instructorCode2", "surveyAnswer2",
"testIdentifier2", "testAnswers2");
OlccStudent student3 = new OlccStudent(3L,3L, "firstName3", "middleInitial3", "lastName3", "email3",
"streetAdress3", "city3", "state3", "zip3", "testDate3", "phoneNumber3", "dateOfBirth3",
"socialSecurityNumber3", "testForm3", "providerCode3", "instructorCode3", "surveyAnswer3",
"testIdentifier3", "testAnswers3");
OlccStudent student4 = new OlccStudent(4L, 4L, "firstName4", "middleInitial4", "lastName4", "email4",
"streetAdress4", "city4", "state4", "zip4", "testDate4", "phoneNumber4", "dateOfBirth4",
"socialSecurityNumber4", "testForm4", "providerCode4", "instructorCode4", "surveyAnswer4",
"testIdentifier4", "testAnswers4");
olccStudentsList.add(student1);
olccStudentsList.add(student2);
olccStudentsList.add(student3);
olccStudentsList.add(student4);
return olccStudentsList;
}
private String validate(OlccStudent student) {
String firstName = student.getFirstName();
String middleInitial = student.getMiddleInitial();
String lastName = student.getLastName();
String streetAddress = student.getStreetAddress();
String city = student.getCity();
String state = student.getState();
String zip = student.getZip();
String dateOfBirth = student.getDateOfBirth();
String ssn = student.getSocialSecurityNumber();
String phoneNumber = student.getPhoneNumber();
String testDate = student.getTestDate();
String testForm = student.getTestForm();
String testAnswers = student.getTestAnswers();
String providerCode = student.getProviderCode();
String instructorCode = student.getInstructorCode();
StringBuilder errorBuilder = new StringBuilder();
//The predicate 'negate' method returns a predicate that represents the logical negation or opposite
if (isValidFirstName().negate().test(student)) {
String error = "Invalid FirstName: " + firstName;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidMiddleInitial().negate().test(student)) {
String error = "Invalid Middle Initial: " + middleInitial;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidLastName().negate().test(student)) {
String error = "Invalid LastName: " + lastName;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidStreetAddress().negate().test(student)) {
String error = "Invalid StreetAddress: " + streetAddress;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidCity().negate().test(student)) {
String error = "Invalid City: " + city;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidState().negate().test(student)) {
String error = "Invalid State: " + state;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidZip().negate().test(student)) {
String error = "Invalid Zip: " + zip;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidDateOfBirth().negate().test(student)) {
String error = "Invalid DateOfBirth: " + dateOfBirth;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidSsn().negate().test(student)) {
String error = "Invalid SSN: " + ssn;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidTestDate().negate().test(student)) {
String error = "Invalid TestDate: " + testDate;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidTestAnswers().negate().test(student)) {
String error = "Invalid TestAnswers: " + testAnswers;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidProviderCode().negate().test(student)) {
String error = "Invalid ProvideCode: " + providerCode;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidInstructorCode().negate().test(student)) {
String error = "Invalid InstructorCode: " + instructorCode;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (errorBuilder.length() > 0) {
errorBuilder.deleteCharAt(errorBuilder.length() - 1);
}
return errorBuilder.toString().trim();
}
public Map<OlccStudent, String> getInvalidStudentsMap(List<OlccStudent> studentsList) throws Exception {
Map<OlccStudent, String> map = studentsList.stream()
// Step 1: Validate each student, keeping a track of any error message generated.
.collect(Collectors.toMap(Function.identity(), student -> validate(student)))
// Step 2: Keep only those that have an error message associated.
.entrySet()
.stream()
.filter(entry -> entry.getValue() != null)
// Step 3: Generate a Map.
.collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue()));
return map;
}
I would create a
ValidationRule
class to stick together validation predicate and error message formatter:Now
getAllRules
will look like this:And you can get the map of invalid students in the following way: