I have created the following method which returns a Triple of strings. However, I don't like the way I've written it because I think I've put in too many Npe checks making it unreadable.
private Triplet<String, String, String> getInfoFromTable(Person person) {
StringBuilder idWithText = new StringBuilder();
String idText;
Date time = null;
Level level;
Exercise exerciseRecord = getExercise(person);
if (exerciseRecord != null && exerciseRecord.getId() != null) {
if(exerciseRecord.getLevel1() != null && exerciseRecord.getLevel2() != null){
level = new Level(exerciseRecord.getLevel1(), exerciseRecord.getLevel2());
} else {
level = new Level("1", "1");
}
idText = getIdText(level, exerciseRecord.getId());
if(!Strings.isNullOrEmpty(idText)) {
idWithText = idWithText.append(exerciseRecord.getId()).append(" " + idText);
}
if (exerciseRecord.getTime() != null) {
time = exerciseRecord.getTime().toDate();
}
return new Triplet<>(idWithText.toString(), "1", formatTime(time));
}
return new Triplet<>("", "", "");
}
Ηow can I make the above code look simpler? I've seen a little use of Optional but I don't know if it's good to use them in my case. Could someone help with the method refactor?
You need to split the huge method into several simple, it will decrease complexity.
private Triplet<String, String, String> getInfoFromTable(Person person) {
Exercise exerciseRecord = getExercise(person);
if (exerciseRecord != null && exerciseRecord.getId() != null) {
return new Triplet<>(getIdWithText(exerciseRecord, getLevel(exerciseRecord)), "1", formatTime(exerciseRecord.getTime()));
}
return new Triplet<>("", "", "");
}
private String formatTime(LocalTime time) {
if (time == null) {
return "";
}
return formatTime(time.toDate());
}
private Level getLevel(Exercise exerciseRecord) {
Level level;
if(exerciseRecord.getLevel1() != null && exerciseRecord.getLevel2() != null){
level = new Level(exerciseRecord.getLevel1(), exerciseRecord.getLevel2());
} else {
level = new Level("1", "1");
}
return level;
}
private String getIdWithText(Exercise exerciseRecord, Level level) {
String idWithText = "";
String idText = getIdText(level, exerciseRecord.getId());
if(!Strings.isNullOrEmpty(idText)) {
idWithText = String.format("%s %s", exerciseRecord.getId(), idText);
}
return idWithText;
}
Related
Trying to build a search criteria query parsing using StringTokenizer.
The query goes like
(key:operator:value)
(key:operator:value) and (key:operator:value)
I have tried
Stack<Object> stack = new Stack<>();
StringTokenizer tokenizer = new StringTokenizer(filterString, "()", true);
while (tokenizer.hasMoreElements()) {
String token = tokenizer.nextToken();
if (isOpenBrace(token)) {
stack.push(token);
} else if (isCloseBrace(token)) {
Object preVal = null;
Criteria criteria = null;
String operator = null;
while (!isOpenBrace(stack.peek())) {
Object top = stack.pop();
if (isString(top)) {
String op = (String) top;
if (isAnd(op) || isOr(op)) {
operator = (String) top;
} else {
throw new Exception("Invalid operand");
}
} else if (isCriteria(top)) {
if (preVal != null) {
if (isAnd(operator)) {
top = ((Criteria) top).and((Criteria) preVal);
criteria = (Criteria) top;
preVal = top;
} else if (isOr(operator)) {
top = ((Criteria) top).or((Criteria) preVal);
criteria = (Criteria) top;
preVal = top;
}
} else {
preVal = top;
criteria = (Criteria) preVal;
}
}
}
if (criteria != null) {
stack.pop();
stack.push(criteria);
}
} else if (isAnd(token) || isOr(token)) {
stack.push(token);
} else {
String[] parts = token.split(COLON);
// do rest stuffs
}
if (stack.size() != 1) {
throw new Exception("Invalid filter");
}
Note: The Criteria internal specific.
This works fine as long as there are no () in the value part of the expression.
Im trying to convert the same using split() and regexes so that, the tokens I get is
"(", "key:operator:va(lue)", ")" for string "(key:operator:va(lue)"
I have lots of multiple if-else statements. For code optimization, I need to write one function for all if else logic. As of now my code structure is in below.
input request is in JSONObject(org.json.simple.JSONObject), which have more than 10 values.
String s = (String) inputObj.get("test");
String s1 = (String) inputObj.get("test");
String s2 = (String) inputObj.get("test");
String s3 = (String) inputObj.get("test");
if (s != null && s.trim().isEmpty()) {
if (s1 != null && s1.trim().isEmpty()) {
if (s2 != null && s2.trim().isEmpty()) {
if (s3 != null && s3.trim().isEmpty()) {
if (s4 != null && s4.trim().isEmpty()) {
........
} else {
return;
}
} else {
return;
}
} else {
return;
}
} else {
return;
}
} else {
return;
}
How to avoid this kind of looping and throw an error message in common method.
Advance thanks.
Consider adding all your strings to array or ArrayList of string, and looping thru each entry in it, and check them for null or emptiness.
You can try this.
void main() {
List<String> sList = new ArrayList<>();
sList.add(inputObj.get("test"));
sList.add(inputObj.get("test"));
sList.add(inputObj.get("test"));
sList.add(inputObj.get("test"));
for(String s : sList){
try {
checkString(s);
}catch (Exception e){
//log or print the exception, however you like
}
}
}
void checkString(String s) throws Exception{
if(s!= null && !s.trim().isEmpty()){
//doStuff
}else{
throw new Exception("String is null or empty !!!");
}
}
You should also check this out.
public class YourClass{
private boolean isBlankDataPresent(JSONObject inputObj, String[] keys) throws Exception {
for (String key : keys) {
String input = (String) inputObj.get(key);
if( input == null || input.trim().isEmpty())
throw new Exception(key +" is Empty");
}
return false;
}
public boolean validateData(JSONObject inputObj, String[] keys) throws Exception {
boolean isBlankDataPresent= isBlankDataPresent(inputObj, keys);
if (!isBlankDataPresent) {
// do Your Stuff and return true
}
}
}
public Integer checkIsEmapty(String checkingString){
if(checkingString != null && !checkingString.trim().isEmpty()){
return 1;
}
return 0;
}
public String method(){
String s ="";
String s1 = "hi";
String s2 = "java";
String s3 = null;
String s4 = null;
Integer s1i = checkIsEmapty(s);
Integer s2i = checkIsEmapty(s1);
Integer s3i = checkIsEmapty(s2);
Integer s4i = checkIsEmapty(s3);
Integer s5i = checkIsEmapty(s4);
Integer total = s1i + s2i + s3i + s4i + s5i;
switch (total){
case 1 :
// To DO
case 2 :
// To DO
}
}
in switch used to checking the value, U can pass binary and Integer also
Like #Emre Acre mentioned,
List<String> sList = new ArrayList<>();
sList.add(inputObj.get("test"));
sList.add(inputObj.get("test"));
sList.add(inputObj.get("test"));
sList.add(inputObj.get("test"));
boolean allDataValid = sList
.stream()
.allMatch(s -> s != null && s.trim().isEmpty());
if(allDataValid) {
......
} else {
return;
}
I need to find best matched employee salary in the DB records as:
Name: City: State:
A (null) (null)
A (null) DEL
(null) (null) (null)
A SAKET DEL
Match order should be:
1. NAME = name, STATE = state, CITY = city
2. NAME = name, STATE = state , CITY = NULL
3. NAME = name, STATE = NULL, CITY = NULL
4. NAME = NULL, STATE = NULL, CITY = NULL
Means if in a row where all attributes matches – it should be selected, if we do not have that kind of data we should go to next best option like select state and city as NULL, etc.
My code as below, is giving me correct results but I need a more efficient way.
private static BigDecimal getsalaryForBestMatch(ResultSet results, EmployeeRq request) throws Exception{
BigDecimal salary = null;
BigDecimal salaryWithState = null;
BigDecimal salaryWithName = null;
BigDecimal salaryWithNoMatch = null;
while (results.next()) {
String billerName = results.getString("EMP_NAME") != null ? results.getString("EMP_NAME").trim() : null;
String city = results.getString("CITY") != null ? results.getString("CITY").trim() : null;
String state = results.getString("STATE") != null ? results.getString("STATE").trim() : null;
BigDecimal salaryRslt = null;
if(results.getString("SALARY") != null){
salaryRslt = BigDecimal.valueOf(results.getDouble("SALARY"));
}
if(billerName != null && !billerName.equals("") && billerName.equals(request.getBillPaymentsalaryCalculateInfo().getBillerName())){
if(city != null && !city.equals("") && city.equals(request.getMsgRqHdr().getCity()) &&
state != null && !state.equals("") && state.equalsIgnoreCase(request.getMsgRqHdr().getstate())){
salary = salaryRslt;
break;
} else if((city == null || city.equals("")) && state != null && !state.equals("") &&
state.equalsIgnoreCase(request.getMsgRqHdr().getState())){
salaryWithState = salaryRslt;
} else if((city == null || city.equals("")) && (state == null || state.equals(""))){
salaryWithName = salaryRslt;
}
} else if((billerName == null || billerName.equals("")) && (city == null || city.equals("")) &&
(state == null || state.equals(""))){
salaryWithNoMatch = salaryRslt;
}
}
if(salary != null){
return salary;
} else if(salaryWithState != null){
salary = salaryWithState;
} else if(salaryWithName != null){
salary = salaryWithName;
} else if(salaryWithNoMatch != null){
salary = salaryWithNoMatch;
}
return salary;
}
EDIT: I dont want to use 3 extra variables: salaryWithState, salaryWithName, salaryWithNoMatch.
I want just to give the general idea how this can be implemented, so I haven't actually tested and checked if it will give you the right salary.
public BigDecimal getSalaryForBestMatch(ResultSet resultSet, PaymentSalaryInfo paymentSalaryInfo) {
Map<String, Supplier<String>> m1 = new HashMap<>();
m1.put("EMP_NAME", paymentSalaryInfo::getBillerName);
m1.put("STATE", paymentSalaryInfo::getState);
m1.put("CITY", paymentSalaryInfo::getCity);
Map<String, Supplier<String>> m2 = new HashMap<>();
m2.put("STATE", paymentSalaryInfo::getState);
m2.put("CITY", paymentSalaryInfo::getCity);
Map<String, Supplier<String>> m3 = new HashMap<>();
m3.put("CITY", paymentSalaryInfo::getCity);
Optional<String> salary = Optional.empty();
while(resultSet.next() && !salary.isPresent()) {
salary = apply(m1, resultSet);
//check salary and then apply(m2, resultSet) ....
}
return salary.isPresent() ? new BigDecimal(salary.get()) : null;
}
public Optional<String> apply(Map<String, Supplier<String>> filter, ResultSet resultSet) {
boolean allMatch = filter.entrySet().stream().allMatch(entry -> {
String value = resultSet.getString(entry.getKey());
return value != null && value.equals(entry.getValue().get());
});
return allMatch ? Optional.of(resultSet.getString("salary")) : Optional.empty();
}
I have written the same logic in a different way with using arrays. If your environment can afford to use arrays, you can use this code. But I have not tested the code.
private static BigDecimal getsalaryForBestMatch(ResultSet results, EmployeeRq request) throws Exception{
BigDecimal salary = null;
int matchCount = 0;
String rBillerName = request.getBillPaymentsalaryCalculateInfo().getBillerName();
String rCity = request.getMsgRqHdr().getCity();
String rState = request.getMsgRqHdr().getstate();
String [] truthArray = new String[] {rBillerName, rCity, rState};
while (results.next()) {
String billerName = results.getString("EMP_NAME") != null ? results.getString("EMP_NAME").trim() : null;
String city = results.getString("CITY") != null ? results.getString("CITY").trim() : null;
String state = results.getString("STATE") != null ? results.getString("STATE").trim() : null;
BigDecimal salaryRslt = results.getString("SALARY") != null ? BigDecimal.valueOf(results.getDouble("SALARY")): null;
String [] testArray = new String[] {billerName, city, state};
int localMatchCount = 0;
for(int i = 0; i < testArray.length; i++) {
if(testArray[i] != null && testArray[i].equals(truthArray[i]))
localMatchCount++;
else {
break;
}
}
if(localMatchCount >= matchCount){
matchCount = localMatchCount;
salary = salaryRslt;
}
}
return salary;
}
I have two foreach loops. One of them contains list of unique emails (outer). I would like to have that as outer loop and increase count by one every time there is a match between an element of outer loop and the inner loop.
My code now:
outer: for (String email : emailsOfContactsWhoFitDynConFilter) {
for (Contact contact : emailClicks.items) {
String[] contactLink = (contact.link).split("\\?", -1);
String queryStringActivity = getQueryStringByName("elqTrackId", contactLink[1]);
if (email.equals(contact.EmailAddress) && contactLink[0].equals(linkInDynamicContentSplit[0])) {
if (queryStringActivity !=null && queryStringDynConLink!=null && queryStringActivity.equals(queryStringDynConLink)){
count++;
break outer;
} else if (queryStringActivity == null || queryStringDynConLink == null) {
System.out.println(" - Missing elqTrackId. But base the same, count++");
count++;
break outer;
}
}
}
}
It works, but problem is these two lines:
String[] contactLink = (contact.link).split("\\?", -1);
String queryStringActivity = getQueryStringByName("elqTrackId", contactLink[1]);
Are executed too many times which consumes a lot of time.
I could reverse the loops, so it would look like this:
outer: for (Contact contact : emailClicks.items) {
String[] contactLink = (contact.link).split("\\?", -1);
String queryStringActivity = getQueryStringByName("elqTrackId", contactLink[1]);
for (String email : emailsOfContactsWhoFitDynConFilter) {
if (email.equals(contact.EmailAddress) && contactLink[0].equals(linkInDynamicContentSplit[0])) {
if (queryStringActivity !=null && queryStringDynConLink!=null && queryStringActivity.equals(queryStringDynConLink)){
count++;
break outer;
} else if (queryStringActivity == null || queryStringDynConLink == null) {
System.out.println(" - Missing elqTrackId. But base the same, count++");
count++;
break outer;
}
}
}
}
That would be much faster, but my count++ would happen more times than I want, it wouldn't be +1 per unique email.
There are a couple good options here, but the first would be to simply cache the String[]. This is a valuable lesson in why you should use methods instead of members.
I suggest having a method of contact.getLinkCache() method, implemented something like I have below. This gives you the benefit of not splitting over and over again (there is a clone in there to protect the data, but clone is a pretty fast method, and unless you've identified this as being too slow, you should probably go with this.
class Contact {
String link;
String[] linkSplitCache;
public void setLink(String link) {
this.link = link;
this.linkSplitCache = null;
}
public String getLink() {
return link;
}
public String[] getLinkCache() {
if(linkSplitCache == null) {
linkSplitCache = link.split("\\?",-1);
}
// return linkSplitCache; // could corrupt!
return linkSplitCache.clone(); // pretty fast array copy
}
}
If it is too slow, then you would want some kind of map to cache it in, and this would probably be outside the Contact class.
Map<Contact, String[]> linkSplitCache = new HashMap<>();
outer: for (Contact contact : emailClicks.items) {
String[] contactLink = linkSplitCache.get(contact);
if(contactLink == null) {
contactLink = (contact.link).split("\\?", -1);
linkSplitCache.put(contact,contactLink);
}
// rest of loop here
With a great help from #corsiKlause Ho Ho Ho I could come to the solution:
Map<String, String[]> linkSplitCache = new HashMap<>();
int count = 0;
String[] linkInDynamicContentSplit = linkInDynamicContent.split("\\?", -1);
String queryStringDynConLink = getQueryStringByName("elqTrackId", linkInDynamicContentSplit[1]);
if (emailClicks != null && emailsOfContactsWhoFitDynConFilter != null) {
for (String email : emailsOfContactsWhoFitDynConFilter) {
inner: for (Contact contact : emailClicks.items) {
String[] contactLink = linkSplitCache.get(contact.EmailAddress);
if (contactLink == null){
contactLink = (contact.link).split("\\?", -1);
contactLink[1] = getQueryStringByName("elqTrackId", contactLink[1]);
linkSplitCache.put(contact.EmailAddress, contactLink);
}
if (email.equals(contact.EmailAddress) && contactLink[0].equals(linkInDynamicContentSplit[0])) {
if (contactLink[1] !=null && queryStringDynConLink!=null && contactLink[1].equals(queryStringDynConLink)){
count++;
break inner; // this excludes link clicks which were done
// twice by the same person
} else if (contactLink[1] == null || queryStringDynConLink == null) {
System.out.println(" - Missing elqTrackId. But base the same, count++");
count++;
break inner;
}
}
}
}
}
Basically what I did was adding the link into the HashMap with the unique key Email address, which makes sure that I don't do the same operation more than once where it's not necessary.
Basically I just want an error message when there are no more records to show in the array list. What do I need to tweak?
public void nextRecord()
{
if(records.size() > 0)
{
recordCount++;
if(records.get(recordCount) != null)
{
String[] array = records.get(recordCount).split(",");
String item = array[0].trim().replaceAll("\"", "");
String number = array[1].trim();
String cost = array[2].trim();
String amnt = array[3].trim();
txtItem.setText(item);
txtNumber.setText(number);
txtCost.setText(cost);
txtAmount.setText(amnt);
}
}
else if (records.get(recordCount) == null)
{
JOptionPane.showMessa
Check the size of arrayList before calling get() like,
if(recordCount< records.size() && records.get(recordCount) != null)
{
//Do the processing
} else {
JOptionPane.showMessa
}