Based on this question and this answer, I decided to try the enum approach to do some more conversions I need in my application. This time I required to convert between the different field naming conventions I use in my application:
- SQL naming convention (used for my field names in SQL DBs): field_name.
- Java naming convention (used for variables, i.e. in beans): fieldName.
- TITLE naming convention (used for display purposes, i.e. in GUI): Field Name.
An example of where would I need this type of conversion is when I try to parse some error messages I get from some library, and try to present them in a readable format to the final user. In this case I would need to get the field name referenced in the error message (which uses the SQL naming convention, because this library refers to the fields as they are found in the DB) and present it to the user in TITLE format.
Another example would be a matching I do between file headers in a CSV file (in the headers I accept any of the three naming conventions) and the field structure required for the CSV processing to be successful, which I get from a Java Bean whose fields (named using my JAVA convention) represent the list of headers I need to get on the file (with their expected data types). (This design is the outcome of several comments made in that same question).
Here is my working enum:
public enum FieldNameConvention {
SQL {
@Override
public String getName(String name) {
return convertName(this, name);
}
},
JAVA {
@Override
public String getName(String name) {
return convertName(this, name);
}
},
TITLE {
@Override
public String getName(String name) {
return convertName(this, name);
}
};
public abstract String getName(String fieldName);
private static String convertName(FieldNameConvention type, String name){
StringBuilder convertedName = new StringBuilder();
// first character is always upper or lower, depending on the type
char currentChar = name.charAt(0);
convertedName.append((type == TITLE) ? toUpper(currentChar) : toLower(currentChar));
for(int i = 1; i < name.length(); i++){
currentChar = name.charAt(i);
if(isSeparator(currentChar)){
// separate if in order to clean trailing separators
if(++i < name.length()){
currentChar = name.charAt(i);
String nextChars = getReplacementChars(type, currentChar);
convertedName.append(nextChars);
}
}else if(isUpper(currentChar)){
convertedName.append(getReplacementChars(type, currentChar));
}else{
convertedName.append(currentChar);
}
}
return convertedName.toString();
}
private static String getReplacementChars(FieldNameConvention type, char nextChar) {
String replacementChars = "";
switch(type){
case SQL:
replacementChars = "_" + toLower(nextChar);
break;
case JAVA:
replacementChars = "" + toUpper(nextChar);
break;
case TITLE:
replacementChars = " " + toUpper(nextChar);
}
return replacementChars;
}
private static boolean isSeparator(char value){
return value == '_' || value == ' ';
}
private static boolean isUpper(char value){
return Character.isUpperCase(value);
}
private static char toUpper(char value){
return Character.toUpperCase(value);
}
private static char toLower(char value){
return Character.toLowerCase(value);
}
}
And this is how I use it to get the field name in the required convention:
String fieldNameSQL = FieldNameConvention.SQL.getName(fieldName);
String fieldNameJava = FieldNameConvention.JAVA.getName(fieldName);
String fieldNameTitle = FieldNameConvention.TITLE.getName(fieldName);
My questions are:
- I would appreciate feedback regarding my coding style, the decision to use enums for this type of task and the solution I am trying to implement to the naming conversion problem I am facing (do you face this same problem? How do you approach it?).
- Do I need to program more defensively (check for several spaces/underscores in malformed names, null/empty names, etc)? Or should my contract just process names properly formed (according to one of the three conventions above) and produce unexpected results when something else is provided to it?
- And as a side question, would you use lambdas here? How? How can I improve this code further?
Also, am I getting 'too clever' with these design decisions? My ultimate goal is to become very good at producing simple, understandable and correct code!