So I got a .csv over which I am iterating and creating objects based on the columns. Now, in the constructor of the to-be-generated object I'm checking a few conditions and throwing Exceptions if said conditions are not met.
Now I've been asking myself - assuming there are some objects in that list that would cause an Exception to be thrown, would there be any possibility to stop going through the constructor of the about-to-be-created-object and simply go to the next line in the .csv and continue building my list?
So in a nutshell:
go over a .csv
build objects based on columns
if an object cannot be created (because an Exception is being thrown in the constructor), ignore it and go to the next element in the list
Is this possible?
Thanks!
I actually found a solution. I put the line, where I was getting my data from .csv in a try-catch-block (using return Object xyz) - that way, the program wouldn't terminate. However, as this line is in a function that has to return an object, I needed to return an object outside of the try-catch-block, which is why I returned null. Now the list in my main was being filled with valid objects, but also with a few null objects.
I then "removed" the null-objects from the list in my main by filtering out all null-objects while filling up my list, using .stream().map(SimpleCsvParser::parseLine).filter(p -> p != null).collect(Collectors.toList());.
Thanks!
I needed to return an object outside of the try-catch-block, which is why I returned null.
Re-organize your code so that you do not always have to return a new object. Trap for the new object creation failure. If exception thrown, skip that particular loop when it comes to adding to the collection of new objects.
Let's make a Person class for a demo. Note how in the constructor we look for valid data, throwing an IllegalArgumentException if not received. This is called data-validation.
package work.basil.example;
import java.util.Objects;
public class Person
{
public String givenName, surname;
// -------| Constructors |------------
public Person ( String givenName , String surname )
{
if ( Objects.isNull( givenName ) || givenName.isEmpty() ) { throw new IllegalArgumentException();}
if ( Objects.isNull( surname ) || surname.isEmpty() ) { throw new IllegalArgumentException();}
this.givenName = givenName;
this.surname = surname;
}
// -------| Accessors |------------
// Read-only, no setters.
public String getGivenName ( )
{
return givenName;
}
public String getSurname ( )
{
return surname;
}
// -------| Object |-----------------
#Override
public boolean equals ( Object o )
{
if ( this == o ) return true;
if ( o == null || getClass() != o.getClass() ) return false;
Person person = ( Person ) o;
return givenName.equals( person.givenName ) &&
surname.equals( person.surname );
}
#Override
public int hashCode ( )
{
return Objects.hash( givenName , surname );
}
#Override
public String toString ( )
{
return "Person{ " +
"givenName='" + givenName + '\'' +
" | surname='" + surname + '\'' +
" }";
}
}
And some fake data. We expect failure on that third line, where the first name is Zero but the last name field is blank. Our Person class requires two String objects that are both non-null and not-empty.
String input =
"Alice,Anderson\r\n" + // Standard CSV requires CRLF as newline.
"Bob,Barker\r\n" +
"Zero,\r\n" +
"Carol,Carrington";
Use the Apache Commons CSV library to do the work of reading our input.
➥ Notice the inner try-catch around the calls to csvRecord.get and the new Person. If those lines fail, we skip adding a Person object to our List. The loop moves on to the next input from the CSV file.
At no point are trying to juggle a null object of type Person. If we do not have a valid Person object at the ready, we move on.
List < Person > persons = new ArrayList <>();
CSVFormat format = CSVFormat.RFC4180;
try ( // Try-with-resources syntax used here, to automatically close the `Reader` whether or not exceptions thrown.
Reader reader = new StringReader( input ) ;
)
{
CSVParser parser = new CSVParser( reader , format );
for ( CSVRecord csvRecord : parser ) // Each line of CSV is parsed into a `CSVRecord` object.
{
try
{
String givenName = csvRecord.get( 0 ); // Annoying zero-based index number.
String surname = csvRecord.get( 1 ); // `CSVRecord::get` throws `ArrayIndexOutOfBoundsException` if no value found for that index.
Person person = new Person( givenName , surname );
persons.add( person ); // If exception is thrown during `CSVRecord::get` or thrown during the construction of the `Person` object, this line of code is not executed.
}
catch ( ArrayIndexOutOfBoundsException | IllegalArgumentException | NullPointerException e )
{
// Log the issue, and move on to the next loop.
System.out.println( "INFO Import failed on row # " + csvRecord.getRecordNumber() + ". Exception: " + e );
// Be aware that if your CSV input has multi-line values, the returned record
// number does *not* correspond to the current line number of the parser that created this record.
}
}
}
catch ( IOException e )
{
e.printStackTrace();
}
System.out.println( "persons = " + persons );
When run.
INFO Import failed on row # 3. Exception: java.lang.IllegalArgumentException
persons = [Person{ givenName='Alice' | surname='Anderson' }, Person{ givenName='Bob' | surname='Barker' }, Person{ givenName='Carol' | surname='Carrington' }]
Related
I am a computer science university student working on my first 'big' project outside of class. I'm attempting to read through large text files (2,000 - 3,000 lines of text), line by line with buffered reader. When a keyword from a list of enums is located, I want it to send the current line from buffered reader to its appropriate method to be handled appropriatley.
I have a solution, but I have a feeling in my gut that there is a much better way to handle this situation. Any suggestions or feedback would be greatly appreciated.
Current Solution
I am looping through the the list of enums, then checking if the current enum's toString return is in the current line from buffered reader using the String.contains method.
If the enum is located, the enum is used in a switch statement for the appropriate method call. (I have 13 total cases just wanted to keep the code sample short).
try (BufferedReader reader = new BufferedReader(new FileReader(inputFile.getAbsoluteFile()))){
while ((currentLine = reader.readLine()) != null) {
for (GameFileKeys gameKey : GameFileKeys.values()) {
if (currentLine.contains(gameKey.toString())) {
switch (gameKey) {
case SEAT -> seatAndPlayerAssignment(currentTableArr, currentLine);
case ANTE -> playerJoinLate(currentLine);
}
}
}
}
}
Previous Solution
Originally, I had a nasty list of if statements checking if the current line contained one of the keywords and then handled it appropriatley. Clearly that is far from optimal, but my gut tells me that my current solution is also less than optimal.
try (BufferedReader reader = new BufferedReader(new FileReader(inputFile.getAbsoluteFile()))){
while ((currentLine = reader.readLine()) != null) {
if(currentLine.contains(GameFileKey.SEAT){
seatAndPlayerAssignment(currentTableArr, currentLine);
}
else if(currentLine.contains(GameFileKey.ANTE){
playerJoinLate(currentLine);
}
}
}
Enum Class
In case you need this, or have any general feedback for how I'm implementing my enums.
public enum GameFileKeys {
ANTE("posts ante"),
SEAT("Seat ");
private final String gameKey;
GameFileKeys(String str) {
this.gameKey = str;
}
#Override
public String toString() {
return gameKey;
}
}
I cannot improve over the core of your code: the looping on values() of the enum, performing a String#contains for each enum object’s string, and using a switch. I can make a few minor suggestions.
I suggest you not override the toString method on your enum. The Object#toString method is generally best used only for debugging and logging, not logic or presentation.
Your string passed to constructor of the enum is likely similar to the idea of a display name commonly seen in such enums. The formal enum name (all caps) is used internally within Java, while the display name is used for display to the user or exchanged with external systems. See the Month and DayOfWeek enums as examples offering a getDisplayName method.
Also, an enum should be named in the singular. This avoids confusion with any collections of the enum’s objects.
By the way, looks like you have a stray SPACE in your second enum's argument.
At first I thought it would help to have a list of all the display names, and a map of display name to enum object. However, in the end neither is needed for your purpose. I kept those as they might prove interesting.
public enum GameFileKey
{
ANTE( "posts ante" ),
SEAT( "Seat" );
private String displayName = null;
private static final List < String > allDisplayNames = Arrays.stream( GameFileKey.values() ).map( GameFileKey :: getDisplayName ).toList();
private static final Map < String, GameFileKey > mapOfDisplayNameToGameFileKey = Arrays.stream( GameFileKey.values() ).collect( Collectors.toUnmodifiableMap( GameFileKey :: getDisplayName , Function.identity() ) );
GameFileKey ( String str ) { this.displayName = str; }
public String getDisplayName ( ) { return this.displayName; }
public static GameFileKey forDisplayName ( final String displayName )
{
return
Objects.requireNonNull(
GameFileKey.mapOfDisplayNameToGameFileKey.get( displayName ) ,
"None of the " + GameFileKey.class.getCanonicalName() + " enum objects has a display name of: " + displayName + ". Message # 4dcefee2-4aa2-48cf-bf66-9a4bde02ac37." );
}
public static List < String > allDisplayNames ( ) { return GameFileKey.allDisplayNames; }
}
You can use a stream of the lines of your file being processed. Just FYI, not necessarily better than your code.
public class Demo
{
public static void main ( String[] args )
{
Demo app = new Demo();
app.demo();
}
private void demo ( )
{
try
{
Path path = Demo.getFilePathToRead();
Stream < String > lines = Files.lines( path );
lines.forEach(
line -> {
for ( GameFileKey gameKey : GameFileKey.values() )
{
if ( line.contains( gameKey.getDisplayName() ) )
{
switch ( gameKey )
{
case SEAT -> this.seatAndPlayerAssignment( line );
case ANTE -> this.playerJoinLate( line );
}
}
}
}
);
}
catch ( IOException e )
{
throw new RuntimeException( e );
}
}
private void playerJoinLate ( String line )
{
System.out.println( "line = " + line );
}
private void seatAndPlayerAssignment ( String line )
{
System.out.println( "line = " + line );
}
public static Path getFilePathToRead ( ) throws IOException
{
Path tempFile = Files.createTempFile( "bogus" , ".txt" );
Files.write( tempFile , "apple\nSeat\norange\nposts ante\n".getBytes() );
return tempFile;
}
}
When run:
line = Seat
line = posts ante
This question already has answers here:
How do I compare strings in Java?
(23 answers)
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 1 year ago.
Previously I created a login system which used a HashMap, but no database. I then ran into the problem of not being able to change the passwords of the system, as they would be reset everytime I booted up the program.
It was like this:
HashMap<String, String> credentials = new HashMap<String, String>();
UserData() {
credentials.put("Manager", "adminpass");
credentials.put("Employee", "employeepass");
credentials.put("Customer", "customerpass");
}
I then realised I want to use text files to store the passwords, so I could edit them and the changes would take effect.
So I created 3 text files. adminpass.txt, employeepass.txt, customerpass.txt
They all contain the passwords which are 'adminpass', 'employeepass', 'customerpass'
With the previous system, I used .equals to compare the password of user input and the real password as it was a string. Now it's a variable, so I am using ==.
Here is my code for logging in:
public void actionPerformed(ActionEvent ae) {
if (ae.getSource() == authenticateButton) {
String roleID = usernameField.getText();
String password = String.valueOf(((JPasswordField) passwordField).getPassword());
if (credentials.containsKey(roleID)) {
if (credentials.get(roleID) == password) {
messageLabel.setForeground(Color.green);
messageLabel.setText("Login successful");
frame.dispose();
m.launch(roleID);
} else {
messageLabel.setForeground(Color.red);
messageLabel.setText("Incorrect password");
}
} else {
messageLabel.setForeground(Color.red);
messageLabel.setText("Incorrect username");
}
}
}
});
I also have read from the text files on startup, assigning what the system has read from the files to the adminpass, employeepass, and customerpass variables.
Everytime I login I get 'incorrect password' even though they're correct.
So I decided to do
System.out.println(credentials.get(roleID))
And it just returns null.
I'm completely confused here, I am grateful if anyone can help or point me in the right direction!
Thanks!
EDIT: when i use .equals(password), i get
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException: Cannot invoke "String.equals(Object)" because the return value of "java.util.HashMap.get(Object)" is null
This syntax makes no sense to me:
UserData() {
You have not provided quite enough detail to pinpoint your problem. But this error message:
"java.util.HashMap.get(Object)" is null
… tells us you likely have a problem with populating your Map.
Map.of
I suggest starting with a simple hard-coded map, to test your code. Take baby steps, building up your app piece by piece.
package work.basil.example.authenticate;
import java.util.Map;
public class App
{
public static void main ( String[] args )
{
App app = new App();
app.demo();
}
private void demo ( )
{
Map < String, String > credentials =
Map.of(
"Manager" , "adminpass" ,
"Employee" , "employeepass" ,
"Customer" , "customerpass"
);
boolean authenticatedManager = credentials.get( "Manager" ).equals( "adminpass" );
boolean authenticatedEmployee = credentials.get( "Employee" ).equals( "employeepass" );
boolean authenticatedCustomer = credentials.get( "Customer" ).equals( "customerpass" );
System.out.println( "credentials = " + credentials );
System.out.println( "authenticatedManager = " + authenticatedManager );
System.out.println( "authenticatedEmployee = " + authenticatedEmployee );
System.out.println( "authenticatedCustomer = " + authenticatedCustomer );
}
}
When run.
credentials = {Employee=employeepass, Customer=customerpass, Manager=adminpass}
authenticatedManager = true
authenticatedEmployee = true
authenticatedCustomer = true
After getting that to work, then replace Map.of hard-coding with a Map populated from your text file or database.
By the way, I'll ignore the issue of saving passwords in plain text. That is obviously the wrong way to manage authentication. I assume you are just learning/practicing, not doing real work for production use.
Populating a map
Here is how to populate a map from this example text file:
Scott,tiger
Alice,password123
Bob,letmein
Example code using NIO.2. See tutorial by Oracle.
String pathText = "/Users/basilbourque/cred.txt";
Path path = Paths.get( pathText );
Map < String, String > tempCredentials = new HashMap <>();
try ( BufferedReader reader = Files.newBufferedReader( path , StandardCharsets.UTF_8 ) )
{
String line = null;
while ( ( line = reader.readLine() ) != null )
{
System.out.println( line );
String[] parts = line.split( "," );
String user = Objects.requireNonNull( parts[ 0 ] ).strip();
String pw = Objects.requireNonNull( parts[ 1 ] ).strip();
if ( user.isBlank() || pw.isBlank() ) { System.out.println( "Add code here to deal with invalid blank inputs. " ); }
tempCredentials.put( user , pw );
}
}
catch ( IOException x )
{
System.err.format( "IOException: %s%n" , x );
}
Map < String, String > credentials = Map.copyOf( tempCredentials ); // Make an unmodifiable map, copied from temporary map.
System.out.println( "credentials = " + credentials );
When run.
Scott,tiger
Alice,password123
Bob,letmein
credentials = {Bob=letmein, Alice=password123, Scott=tiger}
Testing equality
You said:
so I am using ==.
Use == syntax only when asking if two references point to the very same object, the very same chunk of memory.
More commonly you want to compare the content of the objects to see if they are equivalent. For that, use the equals method or some similar method.
In my code I call this method, as a preprocessing step to 'stem' words:
public void getStem(String word)
{
WordnetStemmer stem = new WordnetStemmer( dict );
List<String> stemmed_words = stem.findStems(word, POS.VERB);
System.out.println( stemmed_words.get(0) );
}
Usually everything is good if it gets a normal word (I'm using the Java Wordnet Interface to handle the stemming). The thing is--> I don't always get a normal word, somethings I get things along the lines of isa which is a conjunction of is and a. In such a case that method will return null and my program will crash. How can I defend against this?
This is how I call that code:
public Sentence(String verb, String object, String subject ) throws IOException
{
WordNet wordnet = new WordNet();
this.verb = verb;
this.object = object;
this.subject = subject;
wordnet.getStem( verb );
}
Eventually I want that to read:
this.verb = wordnet.getStem( verb );
I once heard about doing something with null objects, is that applicable here?
I tried this but it didn't work, but I want to do something like this:
public void getStem(String word)
{
WordnetStemmer stem = new WordnetStemmer( dict );
List<String> stemmed_words = stem.findStems(word, POS.VERB);
if( stemmed_words != null)
System.out.println( stemmed_words.get(0) );
else
System.out.println( word );
}
This is the output:
prevent
contain
contain
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 0
at java.util.Collections$EmptyList.get(Collections.java:4454)
at inference_learner.WordNet.getStem(WordNet.java:76)
at inference_learner.Sentence.<init>(Sentence.java:23)
at inference_learner.RegEx.match_regex_patterns(RegEx.java:33)
at inference_learner.ReadFile.readFile(ReadFile.java:30)
at inference_learner.Main.main(Main.java:38)
That won't work because the List is not null, the List is empty.
You have to do the check like this if (stemmed_words.size() > 0)
try
if( stemmed_words != null && stemmed_words.size() > 0))
System.out.println( stemmed_words.get(0) );
else
System.out.println( word );
}
I am novice to java however, I cannot seem to figure this one out. I have a CSV file in the following format:
String1,String2
String1,String2
String1,String2
String1,String2
Each line are pairs. The 2nd line is a new record, same with the 3rd. In the real word the CSV file will change in size, sometimes it will be 3 records, or 4, or even 10.
My issues is how do I read the values into an array and dynamically adjust the size? I would imagine, first we would have to parse though the csv file, get the number of records/elements, then create the array based on that size, then go though the CSV again and store it in the array.
I'm just not sure how to accomplish this.
Any help would be appreciated.
You can use ArrayList instead of Array. An ArrayList is a dynamic array. ex.
Scanner scan = new Scanner(new File("yourfile"));
ArrayList<String[]> records = new ArrayList<String[]>();
String[] record = new String[2];
while(scan.hasNext())
{
record = scan.nextLine().split(",");
records.add(record);
}
//now records has your records.
//here is a way to loop through the records (process)
for(String[] temp : records)
{
for(String temp1 : temp)
{
System.out.print(temp1 + " ");
}
System.out.print("\n");
}
Just replace "yourfile" with the absolute path to your file.
You could do something like this.
More traditional for loop for processing the data if you don't like the first example:
for(int i = 0; i < records.size(); i++)
{
for(int j = 0; j < records.get(i).length; j++)
{
System.out.print(records.get(i)[j] + " ");
}
System.out.print("\n");
}
Both for loops are doing the same thing though.
You can simply read the CSV into a 2-dimensional array just in 2 lines with the open source library uniVocity-parsers.
Refer to the following code as an example:
public static void main(String[] args) throws FileNotFoundException {
/**
* ---------------------------------------
* Read CSV rows into 2-dimensional array
* ---------------------------------------
*/
// 1st, creates a CSV parser with the configs
CsvParser parser = new CsvParser(new CsvParserSettings());
// 2nd, parses all rows from the CSV file into a 2-dimensional array
List<String[]> resolvedData = parser.parseAll(new FileReader("/examples/example.csv"));
// 3rd, process the 2-dimensional array with business logic
// ......
}
tl;dr
Use the Java Collections rather than arrays, specifically a List or Set, to auto-expand as you add items.
Define a class to hold your data read from CSV, instantiating an object for each row read.
Use the Apache Commons CSV library to help with the chore of reading/writing CSV files.
Class to hold data
Define a class to hold the data of each row being read from your CSV. Let's use Person class with a given name and surname, to be more concrete than the example in your Question.
In Java 16 and later, more briefly define the class as a record.
record Person ( String givenName , String surname ) {}
In older Java, define a conventional class.
package work.basil.example;
public class Person {
public String givenName, surname;
public Person ( String givenName , String surname ) {
this.givenName = givenName;
this.surname = surname;
}
#Override
public String toString ( ) {
return "Person{ " +
"givenName='" + givenName + '\'' +
" | surname='" + surname + '\'' +
" }";
}
}
Collections, not arrays
Using the Java Collections is generally better than using mere arrays. The collections are more flexible and more powerful. See Oracle Tutorial.
Here we will use the List interface to collect each Person object instantiated from data read in from the CSV file. We use the concrete ArrayList implementation of List which uses arrays in the background. The important part here, related to your Question, is that you can add objects to a List without worrying about resizing. The List implementation is responsible for any needed resizing.
If you happen to know the approximate size of your list to be populated, you can supply an optional initial capacity as a hint when creating the List.
Apache Commons CSV
The Apache Commons CSV library does a nice job of reading and writing several variants of CSV and Tab-delimited formats.
Example app
Here is an example app, in a single PersoIo.java file. The Io is short for input-output.
Example data.
GivenName,Surname
Alice,Albert
Bob,Babin
Charlie,Comtois
Darlene,Deschamps
Source code.
package work.basil.example;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVRecord;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class PersonIo {
public static void main ( String[] args ) {
PersonIo app = new PersonIo();
app.doIt();
}
private void doIt ( ) {
Path path = Paths.get( "/Users/basilbourque/people.csv" );
List < Person > people = this.read( path );
System.out.println( "People: \n" + people );
}
private List < Person > read ( final Path path ) {
Objects.requireNonNull( path );
if ( Files.notExists( path ) ) {
System.out.println( "ERROR - no file found for path: " + path + ". Message # de1f0be7-901f-4b57-85ae-3eecac66c8f6." );
}
List < Person > people = List.of(); // Default to empty list.
try {
// Hold data read from file.
int initialCapacity = ( int ) Files.lines( path ).count();
people = new ArrayList <>( initialCapacity );
// Read CSV file.
BufferedReader reader = Files.newBufferedReader( path );
Iterable < CSVRecord > records = CSVFormat.RFC4180.withFirstRecordAsHeader().parse( reader );
for ( CSVRecord record : records ) {
// GivenName,Surname
// Alice,Albert
// Bob,Babin
// Charlie,Comtois
// Darlene,Deschamps
String givenName = record.get( "GivenName" );
String surname = record.get( "Surname" );
// Use read data to instantiate.
Person p = new Person( givenName , surname );
// Collect
people.add( p ); // For real work, you would define a class to hold these values.
}
} catch ( IOException e ) {
e.printStackTrace();
}
return people;
}
}
When run.
People:
[Person{ givenName='Alice' | surname='Albert' }, Person{ givenName='Bob' | surname='Babin' }, Person{ givenName='Charlie' | surname='Comtois' }, Person{ givenName='Darlene' | surname='Deschamps' }]
What is the best way to treat null values in Java MessageFormat
MessageFormat.format("Value: {0}",null);
=> Value: null
but actually a "Value: " would be nice.
Same with date
MessageFormat.format("Value: {0,date,medium}",null);
=> Value: null
a "Value: " whould be much more appreciated.
Is there any way to do this? I tried choice
{0,choice,null#|notnull#{0,date,dd.MM.yyyy – HH:mm:ss}}
which results in invalid choice format, what is correct to check for "null" or "not null"?
MessageFormat is only null-tolerant; that is, it will handle a null argument. If you want to have a default value appear instead of something if the value you're working with is null, you have two options:
You can either do a ternary...
MessageFormat.format("Value: {0}", null == value ? "" : value));
...or use StringUtils.defaultIfBlank() from commons-lang instead:
MessageFormat.format("Value: {0}", StringUtils.defaultIfBlank(value, ""));
Yes, you cant. Look at javadoc. Unfortunately, it dind't work with NULL.
Try use optional
Optional.ofNullable(value).orElse(0)
Or see example how to use ChoiceFormat and MessageFormat.
For more sophisticated patterns, you can use a ChoiceFormat to produce correct forms for singular and plural:
MessageFormat form = new MessageFormat("The disk \"{1}\" contains {0}.");
double[] filelimits = {0,1,2};
String[] filepart = {"no files","one file","{0,number} files"};
ChoiceFormat fileform = new ChoiceFormat(filelimits, filepart);
form.setFormatByArgumentIndex(0, fileform);
int fileCount = 1273;
String diskName = "MyDisk";
Object[] testArgs = {new Long(fileCount), diskName};
System.out.println(form.format(testArgs));
The output with different values for fileCount:
The disk "MyDisk" contains no files.
The disk "MyDisk" contains one file.
The disk "MyDisk" contains 1,273 files.
You can create the ChoiceFormat programmatically, as in the above example, or by using a pattern. See ChoiceFormat for more information.
form.applyPattern(
"There {0,choice,0#are no files|1#is one file|1
I need that now in my generator class by a mask.
Reason:
User can save mask with multiple types say "{0} {1,number,000} {2,date,MMyyyy}. And user have data where can be nulls. For result i use MessageFormat class. And want empty string without default 'null' text.
Null check is not that easy, because it will means replace pattern that is used for many records (not just one). And default empty value don't exists for number or date.
So if someone still needs solution. I give my.
Add this methods/classes (I have all in one class)
private static Object[] replaceNulls2NullValues( Object[] values ) {
for ( int i = 0; i < values.length; i++ )
if ( values[i] == null )
values[i] = NullFormatValue.NULL_FORMAT_VALUE;
return values;
}
private static MessageFormat modifyFormaterFormats( MessageFormat formater ) {
formater.setFormats( Arrays.stream( formater.getFormats() ).map( ( f ) -> ( f != null ) ? new NullHandlingFormatWrapper( f ) : null ).toArray( ( l ) -> new Format[l] ) );
return formater;
}
private static final class NullFormatValue {
static final Object NULL_FORMAT_VALUE = new NullFormatValue();
private NullFormatValue() {
}
#Override
public String toString() {
return "";
}
}
private static class NullHandlingFormatWrapper extends Format {
protected Format wrappedFormat;
public NullHandlingFormatWrapper( Format format ) {
wrappedFormat = format;
}
#Override
public StringBuffer format( Object obj, StringBuffer toAppendTo, FieldPosition pos ) {
if ( !( obj instanceof NullFormatValue ) )
wrappedFormat.format( obj, toAppendTo, pos );
return toAppendTo;
}
#Override
public Object parseObject( String source, ParsePosition pos ) {
return wrappedFormat.parseObject( source, pos );
}
}
and for result call
modifyFormaterFormats( new MessageFormat( pattern ) ).format( replaceNulls2NullValues( parameters ) );