While working on Sonar static code analyzer I found some confusing (may be only to me) statement by Sonar on using parentheses.
Below are the few code snippets where Sonar says remove useless parentheses:
line>1 String auth = "Basic "+ com.somepackge.someMethod(((String) (parent.proxyUsername+ ":" + parent.proxyPassword)));
line>2 return rawtime.length() > 3 ? (rawtime.substring(0, rawtime.length() - 2) + rawtime.substring(rawtime.length() - 2, rawtime.length()).toLowerCase()) : rawtime;
though I have replaced above lines with below one to keep Sonar calm :) :
Line>3 String auth = "Basic "+ com.somepackge.someMethod((String) (parent.proxyUsername+ ":" + parent.proxyPassword));
Line>4 return rawtime.length() > 3 ? rawtime.substring(0, rawtime.length() - 2) + rawtime.substring(rawtime.length() - 2, rawtime.length()).toLowerCase() : rawtime;
So the reason for discussing this question is:
Actually using braces/parentheses are way to reduce the confusion so why to remove those parentheses.
What is best way to use parentheses while writing any complex statement in java.
See the line>1 and Line>4 here I think
(String) (parent.proxyUsername+ ":" + parent.proxyPassword)
this part of code should have the braces to avoid confusions but what Sonar expect is something like:
(String) parent.proxyUsername+ ":" + parent.proxyPassword
Any suggestion would be a great help. I got some links regarding this question but those were not much helpful.
First snippet
String auth = "Basic "+ someMethod(((String) (parent.proxyUsername+ ":" + parent.proxyPassword)));
You could rewrite it as:
String auth = "Basic "+ someMethod(parent.proxyUsername+ ":" + parent.proxyPassword);
because the string concatenation operator already does a string conversion. Unless you want a ClassCastException thrown when proxyUsername or proxyPassword are not Strings?
Second snippet
return rawtime.length() > 3 ? (rawtime.substring(0, rawtime.length() - 2) + rawtime.substring(rawtime.length() - 2, rawtime.length()).toLowerCase()) : rawtime;
The parenthesis is indeed unnecessary but the statement is quite unreadable. If you want to keep using the ternary operator I would suggest splitting the statement across lines:
return rawtime.length() > 3
? rawtime.substring(0, rawtime.length() - 2) + rawtime.substring(rawtime.length() - 2, rawtime.length()).toLowerCase()
: rawtime;
or you could revert the condition:
return rawtime.length() <= 3 ? rawtime :
rawtime.substring(0, rawtime.length() - 2) + rawtime.substring(rawtime.length() - 2, rawtime.length()).toLowerCase();
Line 1 has redundant parentheses, but Line 2's parentheses add clarity to the ternary statement.
Whether or not the extra parenthesis in 2 are useful is up for debate - but there's no reason not to remove the redundant ones in 1.
Generally it's best to use extra parenthesis to convey your intent about what the code should do, or to remove ambiguity in the order that things occur.
There is a semantic difference between these two versions:
(String) (parent.proxyUsername+ ":" + parent.proxyPassword)
(String) parent.proxyUsername+ ":" + parent.proxyPassword
In the first, the second set of () already evaluates to a String, implicitly calling parent.proxyUsername.toString() to convert proxyUsername to a String. So the cast is redundant and should be removed IMHO. The second version casts parent.proxyUsername to String, and will throw an exception is it hasn’t got runtime type String (only if it is declared a String is the cast redundant).
I agree that line 2 and 4 are complicated to read no matter if they have the redundant braces or not. Rewrite if you want clarity. That said, redundant braces are sometimes good for clarity IMHO, I do use them occasionally.
the best way is to put your class that you're casting to in a parentheses then the whole part to be converted in another parentheses, then include this whole code in a container parentheses, your code should look like this e.g ((String)(x+y)).
I hope that was helpful, thanks.
Related
I will split up this problem to be more easy to me :
for this expression :
"created":"589c8377576a33706397f3f4"
I write this regex :
output_row.json.replaceAll("\"created\":\"589c8377576a33706397f3f4\"","");
It works ! Now I would like to use a dynamic token e.g. [[:xdigit:]].
I try this but It didn't work !
output_row.json.replaceAll("\"created\":\"[[:xdigit:]]\"","");
Could you advice me, please ?
[[:xdigit:]] is exactly one hex digit. Add the + quantifier to match 1 to n, or the * to match 0 to n hex digits.
Finally I found the answer :
//replace the value of the key created
output_row.json = output_row.json.replaceAll("\"created\":\"[a-zA-Z0-9]+\"","\"created\":\"" + formatted + "\"");
I don't know why this class is not accepted in Talend editor : [[:xdigit:]]not specific to Java perhaps ?
Anyway the topic is closed for me !
Ale
I'm using SonarLint that shows me an issue in the following line.
LOGGER.debug("Comparing objects: " + object1 + " and " + object2);
Side-note: The method that contains this line might get called quite often.
The description for this issue is
"Preconditions" and logging arguments should not require evaluation
(squid:S2629)
Passing message arguments that require further evaluation into a Guava
com.google.common.base.Preconditions check can result in a performance
penalty. That's because whether or not they're needed, each argument
must be resolved before the method is actually called.
Similarly, passing concatenated strings into a logging method can also
incur a needless performance hit because the concatenation will be
performed every time the method is called, whether or not the log
level is low enough to show the message.
Instead, you should structure your code to pass static or pre-computed
values into Preconditions conditions check and logging calls.
Specifically, the built-in string formatting should be used instead of
string concatenation, and if the message is the result of a method
call, then Preconditions should be skipped altoghether, and the
relevant exception should be conditionally thrown instead.
Noncompliant Code Example
logger.log(Level.DEBUG, "Something went wrong: " + message); // Noncompliant; string concatenation performed even when log level too high to show DEBUG messages
LOG.error("Unable to open file " + csvPath, e); // Noncompliant
Preconditions.checkState(a > 0, "Arg must be positive, but got " + a); // Noncompliant. String concatenation performed even when a > 0
Preconditions.checkState(condition, formatMessage()); //Noncompliant. formatMessage() invoked regardless of condition
Preconditions.checkState(condition, "message: %s", formatMessage()); // Noncompliant
Compliant Solution
logger.log(Level.SEVERE, "Something went wrong: %s", message); // String formatting only applied if needed
logger.log(Level.SEVERE, () -> "Something went wrong: " + message); //since Java 8, we can use Supplier , which will be evaluated lazily
LOG.error("Unable to open file {}", csvPath, e);
if (LOG.isDebugEnabled() { LOG.debug("Unable to open file " + csvPath, e); // this is compliant, because it will not evaluate if log level is above debug. }
Preconditions.checkState(arg > 0, "Arg must be positive, but got %d", a); // String formatting only applied if needed
if (!condition) { throw new IllegalStateException(formatMessage()); // formatMessage() only invoked conditionally }
if (!condition) { throw new IllegalStateException("message: " + formatMessage()); }
I'm not 100% sure whether i understand this right. So why is this really an issue. Especially the part about the performance hit when using string concatenation. Because I often read that string concatenation is faster than formatting it.
EDIT: Maybe someone can explain me the difference between
LOGGER.debug("Comparing objects: " + object1 + " and " + object2);
AND
LOGGER.debug("Comparing objects: {} and {}",object1, object2);
is in the background. Because I think the String will get created before it is passed to the method. Right? So for me there is no difference. But obviously I'm wrong because SonarLint is complaining about it
I believe you have your answer there.
Concatenation is calculated beforehand the condition check. So if you call your logging framework 10K times conditionally and all of them evaluates to false, you will be concatenating 10K times with no reason.
Also check this topic. And check Icaro's answer's comments.
Take a look to StringBuilder too.
String concatenation means
LOGGER.info("The program started at " + new Date());
Built in formatting of logger means
LOGGER.info("The program started at {}", new Date());
very good article to understand the difference
http://dba-presents.com/index.php/jvm/java/120-use-the-built-in-formatting-to-construct-this-argument
Consider the below logging statement :
LOGGER.debug("Comparing objects: " + object1 + " and " + object2);
what is this 'debug' ?
This is the level of logging statement and not level of the LOGGER.
See, there are 2 levels :
a) one of the logging statement (which is debug here) :
"Comparing objects: " + object1 + " and " + object2
b) One is level of the LOGGER. So, what is the level of LOGGER object :
This also must be defined in the code or in some xml , else it takes level from it's ancestor .
Now why am I telling all this ?
Now the logging statement will be printed (or in more technical term send to its 'appender') if and only if :
Level of logging statement >= Level of LOGGER defined/obtained from somewhere in the code
Possible values of a Level can be
DEBUG < INFO < WARN < ERROR
(There can be few more depending on logging framework)
Now lets come back to question :
"Comparing objects: " + object1 + " and " + object2
will always lead to creation of string even if we find that 'level rule' explained above fails.
However,
LOGGER.debug("Comparing objects: {} and {}",object1, object2);
will only result in string formation if 'level rule explained above' satisfies.
So which is more smarter ?
Consult this url.
First let's understand the problem, then talk about solutions.
We can make it simple, assume the following example
LOGGER.debug("User name is " + userName + " and his email is " + email );
The above logging message string consists of 4 parts
And will require 3 String concatenations to be constructed.
Now, let's go to what is the issue of this logging statement.
Assume our logging level is OFF, which means that we don't interested in logging now.
We can imagine that the String concatenations (slow operation) will be ALWAYS applied and will not consider the logging level.
Wow, after understanding the performance issue, let's talk about the best practice.
Solution 1 (NOT optimal)
Instead of using String concatenations, we can use String Builder
StringBuilder loggingMsgStringBuilder = new StringBuilder();
loggingMsgStringBuilder.append("User name is ");
loggingMsgStringBuilder.append(userName);
loggingMsgStringBuilder.append(" and his email is ");
loggingMsgStringBuilder.append(email );
LOGGER.debug(loggingMsgStringBuilder.toString());
Solution 2 (optimal)
We don't need to construct the logging message before check the debugging level.
So we can pass logging message format and all parts as parameters to the LOGGING engine, then delegate String concatenations operations to it, and according to the logging level, the engine will decide to concatenate or not.
So, It's recommended to use parameterized logging as the following example
LOGGER.debug("User name is {} and his email is {}", userName, email);
I got a question about printf in a program, in the end i wrote:
System.out.print(area[i]+" ");
It prints:
2.000000000000001 12.0 28.274333882308138
Then I tried to use printf to replace it:
System.out.printf("%4.1f",area[i]+" ");
However, it has errors when it was executed:
f != java.lang.String
I know probably it is because 4.1 is wrong configuration for the value of 2.000000000000001, as there wont be 4 digits wide if I only retrieve 1 digit after the dot, but what does the error message mean?
Due to the concatenation operator +; area[i] + " " is a java.lang.String type. The error on execution is telling you this.
Error messages are your friends. Do learn to appreciate them.
The error may comme from the fact that in your expression :
System.out.printf("%4.1f",area[i]+" ");
You don't print a float, but a String (yes, area[i]+" " is a String).
Try with :
System.out.printf("%4.1f",area[i]);
It should go better, doesn't it ?
I use java and a regexp.
I've made a regexp for password validation :
String PASSWORD_PATTERN_ADVANCED = "^(?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[\\\\##$¤£µ§%&<>,.!:?;~{-|`'_^¨éèçàù)=}()°\"\\]\\[²³*/+]).{8,20}$";
or without the extra slash :
^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[\\##$¤£µ§%&<>,.!:?;~{-|`'_^¨éèçàù)=}()°"\]\[²³*/+]).{8,20}$
whuch means (i may be wrong): at least one digit / at least one lowercase / at least one uppercase / at least one of the special chars listed / with a minimum total length of 8 and a max of 20...
made a test case generating password for success and failure...
success -> OK, all passed
failure -> Almost OK ...
The only password that fails to fail :D are the ones with space in it like :
iF\ !h6 2A3|Gm
¨I O7 gZ2%L£k vd~39
2< A Uw a7kEw6,6S^
cC2c5N#
6L kIw~ Béj7]5
ynRZ #44ç
9A `sè53Laj A
s²R[µ3 9UrR q8n
I am puzzled.
Any thoughts to make it works ?
Thanks
A regex may not be the right tool for the job here.
Regexes are best suited for matching patterns; what you're describing isn't really a pattern, per se; it's more of a rule set. Sure, you may be able to create some regex that helps, but it's a really complex and opaque piece of code which make maintenance a challenge.
A method like this might be a better fit:
public boolean isValidPassword(String password) {
boolean containsLowerCase;
boolean containsUpperCase;
boolean containsInvalid;
boolean containsSpecialChar;
boolean containsDigit;
for(char c: password.toCharArray()) {
containsLowerCase ||= Character.isLowerCase(c);
containsUpperCase ||= Character.isUpperCase(c);
containsDigit ||= Character.isDigit(c);
containsSpecialChar ||= someMethodForDetectingIfItIsSpecial(c);
}
return containsLowerCase &&
containsUpperCase &&
containsSpecialChar &&
containsDigit &&
!containsInvalid &&
password.length >=8 && password.length <=20;
}
You'd need to decide the best way to detect a special character (specialCharArray.contains(c), regular expression, etc).
However, this approach would make adding new rules a lot simpler.
I may be wrong but if you simply don't want spaces then use [^\\s] instead of . in your lookahead.
String PASSWORD_PATTERN_ADVANCED =
"^(?=[^\\s]*\\d)"
+ "(?=[^\\s]*[a-z])"
+ "(?=[^\\s]*[A-Z])"
+ "(?=[^\\s]*[\\\\##$¤£µ§%&<>,.!:?;~{-|`'_^¨éèçàù)=}()°\"\\]\\[²³*/+])"
+ ".{8,20}$";
None of your conditions are stating what can't be in the password, only what must. You need one more condition that combines all the possible valid characters and makes sure all characters in the password are in that list (i.e., (\d|[a-z]|[A-Z]|##$...){8,20} as the final condition). Either that or a list of rejected characters.
So I posted this question
Putting a simple expression language into java
and got a great answer about using ScriptEngine to allow the user to write javascript which I did and it seemed to work
But whilst an expression like
(artist.length>0 ? artist + '-' :'') + (album.length>0 ? album + '-' :'')
works using a full if statement does not
if(artist.length>0) {artist + ':-'} + (album.length>0 ? album + '-' :'')
You might ask why Im doing this, well I was hoping I could use an if:else if:else statement and this was a step towards that
That simply isn't valid javascript. The
<cond> ? <iftrue> : <iffalse>
is the 'expression' form of if-else, and returns the value which can be used.
if {
} else {
}
is the 'statement' version, and is used to execute code, and does NOT return a value.