Sonar gives a major violation error ("Cyclomatic Complexity") for the following code. Following method is used to get the date in a special format, e.g. 14-02-3 (Year-month-weekid).
How can I overcome this violation?
private String finalDateForProject;
public String getFinalDateForProject() {
return finalDateForProject;
}
public void setFinalDateForProject(Integer year,Integer month, Integer weekId) {
String projectMonth;
switch (month) {
case 0: projectMonth = "01";
break;
case 1: projectMonth = "02";
break;
case 2: projectMonth = "03";
break;
case 3: projectMonth = "04";
break;
case 4: projectMonth = "05";
break;
case 5: projectMonth = "06";
break;
case 6: projectMonth = "07";
break;
case 7: projectMonth = "08";
break;
case 8: projectMonth = "09";
break;
case 9: projectMonth = "10";
break;
case 10: projectMonth = "11";
break;
case 11: projectMonth = "12";
break;
default: projectMonth = " ";
break;
}
String yearEdited = year.toString();
yearEdited = yearEdited.replace("20", "");
String projectTrendDate = yearEdited +"-"+projectMonth+"-W"+weekId.toString();
this.finalDateForProject =projectTrendDate;
}
One way to reduce cyclomatic complexity that I see is to replace the switch statement. Just create an array or HashMap that will map month index to number;
public void setFinalDateForProject(Integer year,Integer month, Integer weekId) {
String[] months = new String[] {"01", "02", "03", "04", "05", ...}
// Replace switch statement
String projectMonth = months[month];
// Rest of your code
...
}
Another way to solve this problem will be to replace mapping of numbers to strings with converting integer to String using String.format. Use something like:
String projectMonth = String.format("%02d", month + 1);
A simple way to think about it is, cyclomatic complexity increases the more "branches" you have in your code. So with your switch statement you have a whole lot of branches (13 in fact, if I'm counting right). The switch statement can be replaced with this:
if (month < 0 || month > 11) {
projectMonth = " ";
} else {
month++;
projectMonth = ((month < 10) ? "0" : "") + Integer.toString(month);
}
Note that this still has branches, namely the if/else and ternary ?. But these could probably be removed as well, a good alternative with an array is given in the other answer.
The question shouldn't be "How can I reduce the cyclomatic complexity?", but rather "What is the best way to write this function?". One answer is return String.format("%02d-%02d-W%2d", year-2000, month+1, weekId);.
Related
I'm a relatively new student learning Java programming, but I'd like to ask for some help. The error I'm receiving in my code is stating "variable romanNumeral might not have been initialised."
The intent for this program is for a user to enter a number from 1-39 and then have the appropriate roman numeral value displayed to the user via a dialog box. The code is not complete yet, as I've yet to find a solution to this problem due to the application not letting me compile my code.
Here is the code:
public class exercise4 extends Actor
{
int userNum;
public void act()
{
intInput(userNum);
}
public String intInput(int userNum)
{
String userInput;
String romanNumeral;
userInput = JOptionPane.showInputDialog("Please enter a number to be converted into Roman Numerals.");
userNum = Integer.parseInt(userInput);
switch(userNum)
{
case 1: romanNumeral = "I";
break;
case 2: romanNumeral = "II";
break;
case 3: romanNumeral = "III";
break;
case 4: romanNumeral = "IV";
break;
case 5: romanNumeral = "V";
break;
case 6: romanNumeral = "VI";
break;
case 7: romanNumeral = "VII";
break;
case 8: romanNumeral = "VIII";
break;
case 9: romanNumeral = "IX";
break;
case 10: romanNumeral = "X";
break;
case 11: romanNumeral = "XI";
break;
case 12: romanNumeral = "XII";
break;
case 13: romanNumeral = "XIII";
break;
case 14: romanNumeral = "XIV";
break;
case 15: romanNumeral = "XV";
break;
case 16: romanNumeral = "XVI";
break;
case 17: romanNumeral = "XVII";
break;
case 18: romanNumeral = "XVIII";
break;
case 19: romanNumeral = "XIX";
break;
case 20: romanNumeral = "XX";
break;
case 21: romanNumeral = "XXI";
break;
case 22: romanNumeral = "XXII";
break;
case 23: romanNumeral = "XXIII";
break;
case 24: romanNumeral = "XXIV";
break;
case 25: romanNumeral = "XXV";
break;
case 26: romanNumeral = "XXVI";
break;
case 27: romanNumeral = "XXVII";
break;
case 28: romanNumeral = "XXVIII";
break;
case 29: romanNumeral = "XXIX";
break;
case 30: romanNumeral = "XXX";
break;
case 31: romanNumeral = "XXXI";
break;
case 32: romanNumeral = "XXXII";
break;
case 33: romanNumeral = "XXXIII";
break;
case 34: romanNumeral = "XXXIV";
break;
case 35: romanNumeral = "XXXV";
break;
case 36: romanNumeral = "XXXVI";
break;
case 37: romanNumeral = "XXXVII";
break;
case 38: romanNumeral = "XXXVIII";
break;
case 39: romanNumeral = "XXXIX";
break;
}
return romanNumeral;
}
}
Consider how the code would behave if userNum has a value of 40. The switch statement doesn't have a case that matches such a value, so it would do nothing. Which is what the compiler is complaining about: the variable romanNumeral is not initialized when it's declared, and might not be even after the switch - thus: "variable romanNumeral might not have been initialised."
Two simple fixes: (A) initialize at declaration, e.g. String romanNumeral = "?", or (B) add a default part to the switch, as:
switch(userNum)
{
// other cases first
default: romanNumeral = "?";
}
use default in your switch case. in java you must have initialize a variable before using it. in your code if there is a value where no case matches then the variable will not be initialized.
Add in a default case to your switch statement setting it to some error value. You are getting that warning because it is possible your switch matches none of that and romanNumeral will never get set before it's returned.
String romanNumeral makes a reference to a memory location, but doesn't initialize it (doesn't give it a value). Because you can provide a value of usernum that doesn't cause a value to be set for romanNumeral, you're getting an error. To avoid this, you can add a default case.
In Java, variables defined in some method are not automatically initialized.
Here you have two options:
1. Initialize it using
String romanNumeral = null (or something);
2. Use default in switch
default:
romanNumeral = null (or something);
The error just means that the variable still has no memory allocated in it. so, what you will do to remove the error is just to give it an initial value.This will do :
String romanNumeral = "";
It is important to initialize the variable, object instances or any data structure you use. Sometimes it gives the null error but sometimes it does not even give an error and can give wrong values.
Your question have been answered above but I would like to suggest a modification. Following this table, you can make a HashMap of the roman numbers:
Decimal value (v) Roman numeral (n)
1 I
4 IV
5 V
9 IX
10 X
40 XL
50 L
90 XC
100 C
400 CD
500 D
900 CM
1000 M
Using the hashmap you can calculate the roman number for particular integer.Check the code just to give you an idea to get started:
public static void main(String []args){
int c = 39;int temp =0;
String roman = "";
if(c<40 && C>10)
{
temp = c/10;
c = c%10;
for(int i=0;i<temp;i++)
{
roman = roman+map.get(10);
}
}
if(c<10 && c>5)
{
if(c==9)
{
roman = roman+map.get(9);
}else{
temp = c/5;
c = c%5;
if(temp==1)
roamn += map.get(5);
for(int i=0;i<c;i++)
{
roman = roman+map.get(1);
}
//again you will have to check a case for four the way I did for 9
}
}
}
System.out.println(roman);
}
I am trying to do the following for a Java switch method with a series of JUnit Asserts but am stuck on using "less than" and "greater than" for two cases (see string/int error below), and am not sure how to use the ">" and "<" in my cases.
Here is the exercise followed by my code, followed by the error.
/*
Create a method which uses a switch statement to return a String representing the int passed in as a parameter to the method:
• given 1 return "One"
• given 2 return "Two"
• given 3 return "Three"
• given 4 return "Four"
• given an integer > 4, return "Too big"
• given an integer < 1, return "Too small"
*/
#Test
public void switchIntExample() {
assertEquals("One", stringRepInt(1));
assertEquals("Two", stringRepInt(2));
assertEquals("Three", stringRepInt(3));
assertEquals("Four", stringRepInt(4));
assertEquals("Too big.", stringRepInt(>4));
// assertEquals("Too small.", stringRepInt(<4));
}
//Switch statement for above:
public String stringRepInt(int numberSize) {
String numVar = null;
switch (numberSize) {
case 1:
numVar = "One";
break;
case 2:
numVar = "Two";
break;
case 3:
numVar = "Three";
break;
case 4:
numVar = "Four";
break;
//TODO: question on how to do LESS THAN and GREATER THAN:
// error line:
case (numVar > 4):
numVar = "Too big.";
break;
default:
break;
}
System.out.println(numVar);
return numVar;
}
ERROR:
Error:(293, 27) java: bad operand types for binary operator '>'
first type: java.lang.String
second type: int
case statements only support constant expressions (you cannot do less than, or greater than, in a case and you can't test numVar - the String - with less than). You can use an if and something like
public String stringRepInt(int numberSize) {
String numVar = null;
if (numberSize > 4) {
numVar = "Too big.";
} else {
switch (numberSize) {
case 1:
numVar = "One";
break;
case 2:
numVar = "Two";
break;
case 3:
numVar = "Three";
break;
case 4:
numVar = "Four";
break;
default:
break;
}
System.out.println(numVar);
return numVar;
}
You should add it to the default section, so like this:
default:
numVar = "Too Big";
break;
The purpose of the default section is to deal with all cases not dealt with by the switch cases. You should be taking advantage of that (as #GreenMatt and #FredK mentioned in comments) by putting the 'if checks' in the default section, as follows:
public String stringRepInt(int numberSize) {
String numVar = null;
switch (numberSize) {
case 1:
numVar = "One";
break;
case 2:
numVar = "Two";
break;
case 3:
numVar = "Three";
break;
case 4:
numVar = "Four";
break;
default:
if(numberSize > 4)
numVar = "Too big";
break;
}
System.out.println(numVar);
return numVar;
}
Further, you could add else if (numberSize < 1) numVar = "Too small"; under the if statement if you want to check for number's smallest than one. This is also important because it prevents your method from returning null. (Which currently happens if the user enters a value less than 1)
The resulting code is as follows:
public String stringRepInt(int numberSize) {
String numVar = null;
switch (numberSize) {
case 1:
numVar = "One";
break;
case 2:
numVar = "Two";
break;
case 3:
numVar = "Three";
break;
case 4:
numVar = "Four";
break;
default:
if(numberSize > 4)
numVar = "Too big";
else
numVar = "Too small";
break;
}
System.out.println(numVar);
return numVar;
}
In the error line you are comparing String and int.
error line: case (numVar > 4): numVar = "Too big."; break;
you have declared numVar as String and comparing that with int value 4. Please correct that or I think you are trying to compare numberSize with value 4. Convert that String to int and compare it.
My instructor requires us to take the package from our code and make it a default package. The only problem is he taught us how to do that through Windows and I have a MacBook so his way isn't working. I can't figure out how to do it. I've attached the code to the bottom in case that will help.
package romannumeralcalculator;
import java.util.*;
public class RomanNumeralCalculator
{
public static void main(String[] args)
{
Scanner input = new Scanner(System.in);
int integer;
do {
System.out.print("Please enter an interger from 1 to 5999. Enter a negative number to exit. \n ->");
integer = input.nextInt();
} while (integer >= 6000);
while (integer == 0) {
System.out.println("");
break;
}
String results = "";
int ones = integer % 10;
int tens = (integer / 10) % 10;
int hundreds = (integer / 100) % 10;
int thousands = (integer / 1000) % 1000;
switch (thousands) {
case 1:
results += "M";
break;
case 2:
results += "MM";
break;
case 3:
results += "MMM";
break;
case 4:
results += "MMMM";
break;
case 5:
results += "MMMMM";
break;
default:
System.out.println("");
}
switch (hundreds) {
case 1:
results += "C";
break;
case 2:
results += "CC";
break;
case 3:
results += "CCC";
break;
case 4:
results += "CD";
break;
case 5:
results += "D";
break;
case 6:
results += "DC";
break;
case 7:
results += "DCC";
break;
case 8:
results += "DCCC";
break;
case 9:
results += "CM";
break;
default:
System.out.println("");
}
switch (tens) {
case 1:
results += "X";
break;
case 2:
results += "XX";
break;
case 3:
results += "XXX";
break;
case 4:
results += "XL";
break;
case 5:
results += "L";
break;
case 6:
results += "LX";
break;
case 7:
results += "LXX";
break;
case 8:
results += "LXXX";
break;
case 9:
results += "XC";
break;
default:
System.out.println("");
}
switch (ones) {
case 1:
results += "I";
break;
case 2:
results += "II";
break;
case 3:
results += "III";
break;
case 4:
results += "IV";
break;
case 5:
results += "V";
break;
case 6:
results += "VI";
break;
case 7:
results += "VII";
break;
case 8:
results += "VIII";
break;
case 9:
results += "IX";
break;
default:
System.out.println("");
}
System.out.println(results);
}
}
In the projects tab of Netbeans in the top-left:
Expand the tree for your Project.
Expand the Source Packages folder.
Expand the package with your java file.
Drag the java file from under the package to the Source Packages folder.
A dialog box will pop up with the title Move Class. On that dialog click the Refactor button.
This should be the same procedure in Windows. I am not sure why your professor told you something different.
I have an assignment where I have to use switch. The program is supposed to terminate when the integer -1 is entered. And any number between 1 and 7 will display a day of the week. any other number between 1 and 7 and -1 will display: "Only numbers from 1 to 7 are accepted".
How can I make the program terminate when -1 is entered. Here is the code so far:
Scanner s = new Scanner(System.in);
String res = "";
System.out.println("Input a number ");
int day = s.nextInt();
if(day==-1){
System.exit(0);
}
switch (day) {
case 1:
res="Today is Sunday";
break;
case 2:
res="Today is Monday";
break;
case 3:
res = "Today is Tuesday";
break;
case 4:
res = "Today is Wednesday";
break;
case 5:
res = "Today is Thursday";
break;
case 6:
res = "Today is Friday";
break;
case 7:
res = "Today is Saturday";
break;
default:
res = "Only numbers from 1 to 7 are accepted ";
break;
}
System.out.println(res);
}
}
updated the code with my solution.
Why do you even need the %? You're already explicitly handling every case...let the default handle everything else.
Switch(day)
{
case 1:
..
default:
}
If you give your code 8, well 8%7 == 1 so you'd get Today is Sunday.
try this check whether the day gets greater then the 1,then send it to switch case.
if(day>=1){
System.exit(0);
}else{
switch(day%7){
case:
}
}
Your program is asking for input 1-7 but it will not work for input 7 as you are switching day % 7, if day = 7 then day % 7 = 0. You need to remove day % 7 and place only day. Then the complete solution will be:
System.out.println("Input a number ");
int day = s.nextInt();
switch (day) {
case -1:
System.exit(0);
break;
case 1:
res="Today is Sunday";
break;
case 2:
res="Today is Monday";
break;
case 3:
res = "Today is Tuesday";
break;
case 4:
res = "Today is Wednesday";
break;
case 5:
res = "Today is Thursday";
break;
case 6:
res = "Today is Friday";
break;
case 7:
res = "Today is Saturday";
default:
res = "Only numbers from 1 to 7 are accepted ";
break;
}
System.out.println(res);
}
As Sage's answer, you can put case -1: System.exit(0); break; or use return statement if the logic is in a method (I think using return statement is more graceful than using System.exit()).
However your logic never reach the 'case 7', since you use 'day %7' it will return remnant after dividing by 7, so it will be between 0 and 6 if day is positive or between -6 and 0 if day is negative
Using a do while should help you get your required behaviour
{
Scanner s = new Scanner(System.in);
String res = "";
Integer day;
do{
System.out.println("Input a number ");
day = s.nextInt();
System.out.println(day);
switch (day) {
case 1:
res="Today is Sunday";
break;
case 2:
res="Today is Monday";
break;
case 3:
res = "Today is Tuesday";
break;
case 4:
res = "Today is Wednesday";
break;
case 5:
res = "Today is Thursday";
break;
case 6:
res = "Today is Friday";
break;
case 7:
res = "Today is Saturday";
default:
res = "Only numbers from 1 to 7 are accepted ";
break;
}
System.out.println(res);
}
while (!day.equals(-1));
}
My class built to output the Month and month number builds and runs fine, but somewhere in my class my monthString is just being assigned null. I cannot see where I have ran into this mistake, and I apologize for my bad code. TY! ~~~
public class MonthClass
{
private int monthNum;
private String monthString;
public MonthClass(int num)
{
monthNum = num;
monthString = monthName(num);
}
public void setmonthNum(int num)
{
monthNum = num;
monthString = monthName(num);
}
public String getmonthName(int num)
{
switch (num)
{
case 1: monthString = "January";
break;
case 2: monthString = "Febuary";
break;
case 3: monthString = "March";
break;
case 4: monthString = "April";
break;
case 5: monthString = "May";
break;
case 6: monthString = "June";
break;
case 7: monthString = "July";
break;
case 8: monthString= "Augest";
break;
case 9: monthString = "September";
break;
case 10: monthString = "October";
break;
case 11: monthString = "November";
break;
case 12: monthString = "December";
break;
default: monthString = "This is not a month, re-enter a number";
break;
}
return monthString;
}
public String monthName(int num)
{
return monthString;
}
public String toString()
{
return "The month number is " + monthNum + " and the month name is " + monthString;
}
}
The problem is that your code at no place modifies monthString, and its value always remains null (default value for reference types).
Instead of monthName you should be calling getmonthName (the conversion from month number to string is happening there):
monthString = getmonthName(num);
Method monthName is only a getter - it doesn't modify the monthString. Also as this method doesn't make use of any argument, you can possibly get rid of that too:
public String monthName() {
return monthString;
}
Also, in the default case in the switch statement, the assignment of non-month string is going to happen silently. You might want to print something on the console as well.