I have the next java implementation, but it has a SonarQube issue and i don't know how to reduce the method.
if (user.getUserId() != null && user.getEmail() != null
&& user.getName() != null) {
return 1;
} else if (user.getUserId() != null && user.getEmail() != null) {
return 2;
} else if (user.getUserId() != null && user.getName() != null) {
return 3;
} else if (user.getUserId() != null) {
return 4;
} else if (user.getEmail() != null && user.getName() != null) {
return 5;
} else if (user.getName() != null) {
return 6;
} else if (user.getEmail() != null) {
return 7;
} else {
return 0;
}
when the user is a Pojo of Users
class Users {
String userId;
String email;
String name;
//getters and setters...
}
This is the real code and issue in SonarQube:
what about Pattern of Chain of Responsibility.
public abstract class Handler {
protected Handler next;
public abstract Integer process(User user);
}
Handler checkIdEmailName = new Handler() {
#Override
public Integer process(User user) {
boolean check = false;
if (check) {
return 1;
}else {
return next.process(user);
}
}
};
Handler checkIdEmail = new Handler() {
#Override
public Integer process(User user) {
boolean check = false;
if (check) {
return 1;
}else {
return next.process(user);
}
}
};
checkIdEmailName.next = checkIdEmail;
Integer result = checkIdEmailName.process(new User());
Break it to another methods would reduce the complexity of the method itself.
Something like
if (user.getUserId() != null && user.getEmail() != null
&& user.getName() != null) {
return 1;
} else if (user.getUserId() != null && user.getEmail() != null) {
return 2;
} else if (user.getUserId() != null && user.getName() != null) {
return 3;
} else if (user.getUserId() != null) {
return 4;
} else {
return yourNewMethod();
}
private int yourNewMethod(User user) {
if (user.getEmail() != null && user.getName() != null) {
return 5;
} else if (user.getName() != null) {
return 6;
} else if (user.getEmail() != null) {
return 7;
} else {
return 0;
}
}
so first of all we should emphasis what cognitive complexity is all about, and how it would make sense to reduce it. Mainly by extracting things which belongs together.
When taking a look at your method with the first 4 conditions, they share a similarity, and it is the check for userId. This means you can easily combine the checks below into one branch. Which will be easier to grasp, and also offers a nice way for extraction
if (user.getUserId() != null) {
if (user.getEmail() != null
&& user.getName() != null) {
return 1;
} else if (user.getEmail() != null) {
return 2;
} else if (user.getName() != null) {
return 3;
} else {
return 4;
}
} else if (user.getEmail() != null && user.getName() != null) {
return 5;
} else if (user.getName() != null) {
return 6;
} else if (user.getEmail() != null) {
return 7;
} else {
return 0;
}
Based on this you can further extract easily like
if (user.getUserId() != null) {
return fancyNameForAdditionalUserIdChecks(user);
} else if (user.getEmail() != null && user.getName() != null) {
return 5;
} else if (user.getName() != null) {
return 6;
} else if (user.getEmail() != null) {
return 7;
} else {
return 0;
}
private int fancyNameForAdditionalUserIdChecks(Users user) {
if (user.getEmail() != null
&& user.getName() != null) {
return 1;
} else if (user.getEmail() != null) {
return 2;
} else if (user.getName() != null) {
return 3;
} else {
return 4;
}
}
Now we only have four main branches in the first if - a developer who does not care about cases where the userid is null, can simple skip it, and he will not need to process all those branches. Where as if needs to, he has only one condition to first check, and the rest is easier to grasp.
To handle such a case, you always should think about, how somebody is reading code. What benefit he gets, and were you can reduce the cognitive load so it will gets easier for the following developer to understand. Machines will never have problem reading our code, but writing code which is easy to read by humans, that is the hard part.
Sidenote:
i would rather rework your Dao methods, and in this case put that logic into the dao, by providing a map of paramaters to the dao, and let the dao handle such a logic. like dao.find(Map<String, Object> params) and you could handle this based on the fields available or not available.
Here is the reduce complexity code,
boolean userIdAvailable = user.getUserId() != null;
boolean emailAvailable = user.getEmail() != null;
boolean nameAvailable = user.getName();
short response = checkAll(userIdAvailable, emailAvailable, nameAvailable);
if (response == 0) {
response = checkEmailAndName(emailAvailable, nameAvailable);
if (response == 0) {
checkUserIdAndName(userIdAvailable, nameAvailable);
}
}
short checkAll(boolean userIdAvailable, boolean emailAvailable, boolean nameAvailable) {
return userIdAvailable && emailAvailable && nameAvailable ? 1 : 0;
}
short checkEach(boolean userIdAvailable, boolean emailAvailable, boolean nameAvailable) {
short response = 0;
if(userIdAvailable) {
response = 4;
} else if(emailAvailable) {
response = 7;
} else if(nameAvailable) {
response = 6;
}
return response;
}
short checkUserIdAndEmail(boolean emailAvailable, boolean userIdAvailable) {
return userIdAvailable && emailAvailable ? 2 : 0
}
short checkEmailAndName(boolean emailAvailable, boolean nameAvailable) {
return emailAvailable && nameAvailable ? 5 : 0
}
short checkUserIdAndName(boolean userIdAvailable, boolean nameAvailable) {
return userIdAvailable && nameAvailable ? 3 : 0
}
Related
I need to search for 7 ID and if 1 is found return this Node. As I can't find any way to search for id.contains I made the code below that meets my need. But it hurts my eyes to look at it. Is there a way to optimize this code ??
As for performance, would it be necessary to use something like node.recycle() in the List to avoid overloading ???
private AccessibilityNodeInfo searchID() {
AccessibilityNodeInfo node = getRootInActiveWindow();
if (node != null) {
List<AccessibilityNodeInfo> id1 = node.findAccessibilityNodeInfosByViewId("com....:id/id1");
List<AccessibilityNodeInfo> id2 = node.findAccessibilityNodeInfosByViewId("com....:id/id2");
List<AccessibilityNodeInfo> id3 = node.findAccessibilityNodeInfosByViewId("com....:id/id3");
List<AccessibilityNodeInfo> id4 = node.findAccessibilityNodeInfosByViewId("com....:id/id4");
List<AccessibilityNodeInfo> id5 = node.findAccessibilityNodeInfosByViewId("com....:id/id5");
List<AccessibilityNodeInfo> id6 = node.findAccessibilityNodeInfosByViewId("com....:id/id6");
List<AccessibilityNodeInfo> id7 = node.findAccessibilityNodeInfosByViewId("com....:id/id7");
if (id1 != null && !id1.isEmpty()) {
return id1.get(0);
} else if (id2 != null && !id2.isEmpty()) {
return id2.get(0);
} else if (id3 != null && !id3.isEmpty()) {
return id3.get(0);
} else if (id4 != null && !id4.isEmpty()) {
return id4.get(0);
} else if (id5 != null && !id5.isEmpty()) {
return id5.get(0);
} else if (id6 != null && !id6.isEmpty()) {
return id6.get(0);
} else {
if (id7 != null && !id7.isEmpty()) {
return id7.get(0);
}
}
}
return null;
}
I know there many similar questions and I have received big help by reading answers to those questions, however I am not able to see how is my client facing this problem. And there is only one client who is facing this problem.
I have a List, and I am sorting that list using Comparator interface. Does any of you see problem with the following code?
private static class BiologySamplesComparator implements Comparator<BiologySample>, Serializable {
#Override
public int compare(BiologySample left, BiologySample right) {
if (left == right || (left != null && right != null && left.getSampleDateTime() == right.getSampleDateTime())) {
return 0;
}
if (left == null || left.getSampleDateTime() == null) {
return 1;
}
if (right == null || right.getSampleDateTime() == null) {
return -1;
}
return right.getSampleDateTime().compareTo(left.getSampleDateTime());
}
}
And this how I am calling this function
Collections.sort(biologySamples, new BiologySamplesComparator());
I know that the main problem in this kind of scenario is Transitivity. However I couldn't figure what is violating that rule.
This how getSampleDateTime() is returning date Fri Apr 09 17:00:00 PDT 2021
Update
This is how I was able to fix my problem.
I hope this helps, I was stuck for so long on this problem.
private static class BiologySamplesComparator implements Comparator<BiologySample>, Serializable {
#Override
public int compare(BiologySample left, BiologySample right) {
if (left == null) {
if (right == null) {
return 0;
} else {
return 1;
}
} else if (right == null) {
return -1;
} else if (left == right) {
return 0;
}
if (left.getSampleDateTime() == null) {
if (right.getSampleDateTime() == null) {
return 0;
} else {
return 1;
}
} else if (right.getSampleDateTime() == null) {
return -1;
} else if (left.getSampleDateTime() == right.getSampleDateTime()) {
return 0;
}
return right.getSampleDateTime().compareTo(left.getSampleDateTime());
}
}
You have a possible inconsistency when comparing a null "sample" to a non-null sample with a null timestamp.
Sample a = null;
Sample b = new Sample(null);
bsc.compare(a, b); // -> 1, a > b
bsc.compare(b, a); // -> 1, b > a
First, you should replace the Date in your sample class with Instant if at all possible, and then make your life simpler by saying this:
public static final Comparator<Sample> ORDER_BY_TIMESTAMP =
Comparator.nullsLast(Comparator.comparing(
Sample::getDateTime,
Comparator.nullsLast(Comparator.naturalOrder())
);
If you can rule out null values, even simpler:
Comparator.comparing(Sample::getDateTime);
I was missing some conditional for some cases and this is how I was able to solve my problem.
private static class BiologySamplesComparator implements Comparator<BiologySample>, Serializable {
#Override
public int compare(BiologySample left, BiologySample right) {
if (left == null) {
if (right == null) {
return 0;
} else {
return 1;
}
} else if (right == null) {
return -1;
} else if (left == right) {
return 0;
}
if (left.getSampleDateTime() == null) {
if (right.getSampleDateTime() == null) {
return 0;
} else {
return 1;
}
} else if (right.getSampleDateTime() == null) {
return -1;
} else if (left.getSampleDateTime() == right.getSampleDateTime()) {
return 0;
}
return right.getSampleDateTime().compareTo(left.getSampleDateTime());
}
}
courtesy of Why does my compare methd throw IllegalArgumentException sometimes?
I wanted to sort list of records in my using multiple comparators.
It contains null values as well for some fields.
I used java collections, to sort it, but it works only for one filed which is places at top.
My code:
Collections.sort(userListResult, new Comparator<UserDto>()
{
#Override
public int compare(UserDto userDto1, UserDto userDto2)
{
if (userDto1.getLastLoginDate() != null && userDto2.getLastLoginDate() != null && userDto2.getLastLoginDate().compareTo(userDto1.getLastLoginDate()) != 0) {
return userDto2.getLastLoginDate().compareTo(userDto1.getLastLoginDate());
}
if (userDto1.getLastLoginDate() != null && userDto2.getLastLoginDate() == null) {
return -1;
}
if (userDto1.getLastLoginDate() == null && userDto2.getLastLoginDate() != null) {
return 1;
}
if (userDto1.getLastActionDate() != null && userDto2.getLastActionDate() != null && userDto2.getLastActionDate().compareTo(userDto1.getLastActionDate()) != 0) {
return userDto2.getLastActionDate().compareTo(userDto1.getLastActionDate());
}
if (userDto1.getLastActionDate() == null && userDto2.getLastActionDate() == null) {
return 0;
}
if (userDto1.getLastActionDate() == null && userDto2.getLastActionDate() != null) {
return 1;
}
if (userDto1.getLastActionDate() != null && userDto2.getLastActionDate() == null) {
return -1;
}
if (userDto1.getLastName() != null && userDto2.getLastName() != null && userDto1.getLastName().compareTo(userDto2.getLastName()) != 0) {
return userDto1.getLastName().compareTo(userDto2.getLastName());
}
if (userDto1.getLastName() != null && userDto2.getLastName() == null) {
return -1;
}
if (userDto1.getLastName() == null && userDto2.getLastName() != null) {
return 1;
}
if (userDto1.getFirstName().compareTo(userDto2.getFirstName()) != 0) {
return userDto1.getFirstName().compareTo(userDto2.getFirstName());
}
if (userDto1.getFirstName() != null && userDto2.getFirstName() == null) {
return -1;
}
if (userDto1.getFirstName() == null && userDto2.getFirstName() != null) {
return 1;
}
return 0;
}
});*/
I wanted to sort descending of LastLoginDate, LastActionDate. Either or both fields might be null in some cases.
And ascending of names. And to display all other null field values(LastLoginDate, LastActionDate) at last.
But when i sort, with this code it takes only the first defined method to sort.
Example, if i have 3 records having same, "LastLoginDate" then it should check sort with "LastActionDate" values. But its not sorted with this logic.
So i added another algorithm to sort, since some fields has null values it is not properly sorted.
My second logic:
if (userDto1.getLastLoginDate() != null && userDto2.getLastLoginDate() != null && userDto2.getLastLoginDate().compareTo(userDto1.getLastLoginDate()) != 0) {
compareLastLoginDate = userDto2.getLastLoginDate().compareTo(userDto1.getLastLoginDate());
}
if (userDto1.getLastActionDate() != null && userDto2.getLastActionDate() != null && userDto2.getLastActionDate().compareTo(userDto1.getLastActionDate()) != 0) {
compareLastActionDate = userDto2.getLastActionDate().compareTo(userDto1.getLastActionDate());
}
if (userDto1.getLastName() != null && userDto2.getLastName() != null && userDto1.getLastName().compareTo(userDto2.getLastName()) != 0) {
compareLastName = userDto1.getLastName().compareTo(userDto2.getLastName());
}
if (compareLastLoginDate == 0) {
return ((compareLastActionDate == 0) ? compareLastName : compareLastActionDate);
}
else {
return compareLastLoginDate;
}
Here i am getting the integer values to compare it "compareLastActionDate, compareLastName, compareLastLoginDate".
I used simple date format to filter the list.
Here is my working code,
public int compare(UserDto userDto1, UserDto userDto2) {
DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
if (userDto1.getLastLoginDate() != null && userDto2.getLastLoginDate() != null && userDto2.getLastLoginDate().compareTo(userDto1.getLastLoginDate()) != 0) {
String Date1 = format.format(userDto1.getLastLoginDate());
String Date2 = format.format(userDto2.getLastLoginDate());
if (Date1.equals(Date2)) {
if (userDto1.getLastActionDate() != null && userDto2.getLastActionDate() != null && userDto2.getLastActionDate().compareTo(userDto1.getLastActionDate()) != 0) {
String Date3 = format.format(userDto1.getLastActionDate());
String Date4 = format.format(userDto2.getLastActionDate());
if (Date3.equals(Date4)) {
if (userDto1.getLastName() != null && userDto2.getLastName() != null && userDto1.getLastName().compareTo(userDto2.getLastName()) != 0) {
return userDto1.getLastName().compareTo(userDto2.getLastName());
}
} else {
return userDto2.getLastActionDate().compareTo(userDto1.getLastActionDate());
}
}
} else {
return userDto2.getLastLoginDate().compareTo(userDto1.getLastLoginDate());
}
}
if (userDto1.getLastLoginDate() != null && userDto2.getLastLoginDate() == null) {
return -1;
}
if (userDto1.getLastLoginDate() == null && userDto2.getLastLoginDate() != null) {
return 1;
}
if (userDto1.getLastLoginDate() == null && userDto2.getLastLoginDate() == null) {
return userDto1.getLastName().compareTo(userDto2.getLastName());
} }
I would recommend you to use the java8 comparators, it provides some helpful methods to compare based in multiple attributes with thenComparing().
If you don't want to / can't use, try two wrap part of that repetitive code in one or multiple custom Comparator or in a helper method.
Here, for simplicity, the following helper method is used (in java8 you can use method references):
public static <R extends Comparable<? super R>> int nullSort(R r1, R r2,
boolean ascending,
boolean nullsFirst) {
R rs1 = ascending ? r1 : r2, rs2 = ascending ? r2 : r1;
if (rs1 != null && rs2 != null) return rs1.compareTo(rs2);
else if (r1 == null && r2 == null) return 0;
else if (r1 == null) return (nullsFirst ? -1 : 1);
else return (nullsFirst ? 1 : -1);
}
In your method, you could use this nullSort() method, to check first for lastLoginDate descending and nulls last (false, false), then lastActionDate again descending and nulls last.
A difference with your comparator is that you stop evaluating here when both are null. It would be better that when lastLoginDate and lastActionDate are nulls you keep ordering by lastName and firstName. If you don't want this, you can add if (result == 0) return 0; after the second if in the method below.
Then after the dates are compared, you compare lastName and then firstName both ascending and nulls last (true, false).
#Override
public int compare(UserDto dto1, UserDto dto2) {
int result;
if ((result = nullSort(dto1.getLastLoginDate(), dto2.getLastLoginDate(),
false, false)) != 0) return result;
if ((result = nullSort(dto1.getLastActionDate(), dto2.getLastActionDate(),
false, false)) != 0) return result;
if ((result = nullSort(dto1.getLastName(), dto2.getLastName(),
true, false)) != 0) return result;
if ((result = nullSort(dto1.getFirstName(), dto2.getFirstName(),
true, false)) != 0) return result;
return 0;
}
This is my code , I need to know if there is any way to reduce the number of lines in the program.
Since there is method calls involved in the if statements I cannot use enum to replace them.
public class AgreementQueryBuilder {
#SuppressWarnings("unchecked")
public static String searchQueryBuilder(FetchAgreementsModel agreementsModel,
#SuppressWarnings("rawtypes") List obj) {
StringBuilder selectQuery = new StringBuilder(
"select *,e.id as agreementid from eglams_agreement e left outer join eglams_rentincrementtype t on e.rent_increment_method=t.id where");
// if statement to check the arguments and build the where criteria
if (agreementsModel.getAgreementId() != null || agreementsModel.getAgreementNumber() != null
|| agreementsModel.getStatus() != null || agreementsModel.getTenantId() != null
|| (agreementsModel.getFromDate() != null && agreementsModel.getToDate() != null)
|| agreementsModel.getTenderNumber() != null || agreementsModel.getTinNumber() != null
|| agreementsModel.getTradeLicense_number() != null)
return null;
boolean isAllFieldsNull = true;
if (agreementsModel.getAgreementId() != null) {
if (isAllFieldsNull == true) {
selectQuery.append(" e.id=?");
isAllFieldsNull = false;
obj.add(agreementsModel.getAgreementId());
}
}
if (agreementsModel.getAgreementNumber() != null) {
if (isAllFieldsNull == true) {
selectQuery.append(" e.agreement_number=?");
isAllFieldsNull = false;
} else
selectQuery.append(" and e.agreement_number=?");
obj.add(agreementsModel.getAgreementNumber());
}
if (agreementsModel.getStatus() != null) {
if (isAllFieldsNull == true) {
selectQuery.append(" e.status=?");
isAllFieldsNull = false;
} else
selectQuery.append(" and e.status=?");
obj.add(agreementsModel.getStatus());
}
if (agreementsModel.getTenantId() != null) {
if (isAllFieldsNull == true) {
selectQuery.append(" e.tenant_id=?");
isAllFieldsNull = false;
} else
selectQuery.append(" and e.tenant_id=?");
obj.add(agreementsModel.getTenantId());
}
if (agreementsModel.getTenderNumber() != null) {
if (isAllFieldsNull == true) {
selectQuery.append(" e.tender_number=?");
isAllFieldsNull = false;
} else
selectQuery.append(" and e.tender_number=?");
obj.add(agreementsModel.getTenderNumber());
}
if (agreementsModel.getTinNumber() != null) {
if (isAllFieldsNull == true) {
selectQuery.append(" e.tin_number=?");
isAllFieldsNull = false;
} else
selectQuery.append(" and e.tin_number=?");
obj.add(agreementsModel.getTinNumber());
}
if (agreementsModel.getTradeLicense_number() != null) {
if (isAllFieldsNull == true) {
selectQuery.append(" e.TradeLicense_number=?");
isAllFieldsNull = false;
} else
selectQuery.append(" and e.TradeLicense_number=?");
obj.add(agreementsModel.getTradeLicense_number());
}
if (agreementsModel.getFromDate() != null) {
if (agreementsModel.getToDate() != null) {
if (isAllFieldsNull == true) {
selectQuery.append(" t.FromDate=?");
isAllFieldsNull = false;
} else
selectQuery.append(" and t.FromDate=?");
obj.add(agreementsModel.getFromDate());
selectQuery.append(" and t.ToDate=?");
obj.add(agreementsModel.getToDate());
}
}
System.err.println(selectQuery);
return selectQuery.toString();
}
}
Look at your code:
if (agreementsModel.getAgreementId() != null || ... )
return null;
So we know that if getAgreementId() != null this method will return null and will not go any further. But right after that you write:
if (agreementsModel.getAgreementId() != null) {
selectQuery.append(" e.id=?");
isAllFieldsNull = false;
obj.add(agreementsModel.getAgreementId());
}
}
We know that this entire block can never be entered, because if getAgreementId() != null was true, the method would have already returned.
So, the short answer to your question is yes, you can make this code much shorter by removing all of the irrelevant blocks of code that will never be called.
The long answer is that you should fix and debug your code to ensure that it actually does what you expect it to do before worrying about whether you can make it shorter. I suspect that means that this if can be removed since it makes all the following blocks useless:
if (agreementsModel.getAgreementId() != null || agreementsModel.getAgreementNumber() != null
|| agreementsModel.getStatus() != null || agreementsModel.getTenantId() != null
|| (agreementsModel.getFromDate() != null && agreementsModel.getToDate() != null)
|| agreementsModel.getTenderNumber() != null || agreementsModel.getTinNumber() != null
|| agreementsModel.getTradeLicense_number() != null)
return null;
But only you can know how your code should work. I suggest testing it to make sure it works before asking how to make it shorter.
I saw that the conditional used to generate an SQL Query. I believe you should change this to more readable, shorter and maintainable code using mapper framework like MyBatis / Hibernate.
Sample of query with conditional using Mybatis below :
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
<!-- add another conditional to add extra filter -->
</select>
On the sample above, we can dynamically generate a query by checking the input bean Blog. If title not null, add title in the criteria,and if authoer is not null, also add author to the criteria, and so on.
Your code all follows the same pattern
if (agreementsModel.getAgreementNumber() != null) {
if (isAllFieldsNull == true) {
selectQuery.append(" e.agreement_number=?");
isAllFieldsNull = false;
} else
selectQuery.append(" and e.agreement_number=?");
obj.add(agreementsModel.getAgreementNumber());
}
Which is: if something isn't null, put that thing in a list; and append a string based on some flag; then set the flag to true.
You can write this as a method:
boolean yourMethod(Object obj, List<Object> objs, String clause, StringBuilder clauses, boolean flag) {
if (obj == null) return flag;
objs.add(obj);
if (!flag) clauses.append(" and ");
clauses.append(clause);
return false;
}
So, you can now replace the above snippet of your code with:
isAllFieldsNull = yourMethod(
agreementsModel.getAgreementNumber(),
obj,
" e.agreement_number=? ",
selectQuery,
isAllFieldsNull);
etc, for the subsequent fields.
How do you count number of branches, in this case branches with even integers. Here's what I have so far. It seems to work for a couple of the cases.
public int evenBranches() {
return evenBranches(overallRoot);
}
private int evenBranches(IntTreeNode root) {
if (root == null) {
return 0;
}
int val = 0;
if (root.left != null) {
val += evenBranches(root.left);
} else if (root.right != null) {
val += evenBranches(root.right);
}
if (root.data % 2 == 0) {
return val + 1;
} else {
return val;
}
}
You can modify the evenBranches() method as below: I think It will cover all edge cases, If any testcase is left, let me know, I will fix it.
public int evenBranches() {
return evenBranches(overallRoot, 0);
}
private int evenBranches(IntTreeNode root, int count) {
if(root == null || (root.left == null && root.right == null)) {
return count;
}
if(root.data % 2 == 0) {
count++;
}
count += evenBranches(root.left, count);
count += evenBranches(root.right, count);
return count;
}
You may need to remove the else condition when checking the occurrences in right branch. Otherwise it will check only one side. eg:
private int evenBranches(IntTreeNode root) {
if (root == null) {
return 0;
}
int val = 0;
if (root.left != null) {
val += evenBranches(root.left);
}
if (root.right != null) {
val += evenBranches(root.right);
}
if (root.data % 2 == 0) {
return val + 1;
} else {
return val;
}
}
You can very well achieve the desired results by using a global variable, and applying BFS (breadth first search) on your tree, in this manner:
int evencount = 0; // global-var.
public int evenBranches() {
evenBranches(overallRoot);
return evencount;
}
private void evenBranches(IntTreeNode root) {
if(!root) return;
if( (root.left || root.right) && (root.data % 2 == 0)){
evencount++;
}
evenBranches(root.left);
evenBranches(root.right);
}