Forbidden APIs of Java

Software developers try to take care of many cases when they develop a software. They make checks for null comparisons, checking to be negative for a function variable which should be positive, etc. They also want to write generic software which can run on Windows, Linux, etc. cause they do not know at where it will be run.
However, there may be still huge problems which haven’t detected. Do you make case insensitive String comparisons at Java? Consider that:

String city = "istanbul";
System.out.println(city.toUpperCase());

You may accept that result will be:

ISTANBUL

On the other hand if you run it at a Turkish locale result will be:

İSTANBUL

This may break your really well designed software! When you upper case an ‘i‘ it will be ‘İ‘ and when you lower case an ‘I‘ it will be ‘ı‘ in Turkish. You can see a similar effect for:

getBytes()

method when you pass different character sets into it.

Problem:

Bear in mind that: Do not use default locale, character sets etc.! OK, but how we can detect such cases? There is a plugin for it: forbiddenapis Add it to your project as described at forbiddenapis wiki. When you run it you will probably have such errors:

Forbidden method invocation: java.text.DecimalFormat#(java.lang.String) [Uses default locale]
Forbidden method invocation: java.lang.String#toLowerCase() [Uses default locale]
Forbidden method invocation: java.util.Scanner#(java.io.InputStream) [Uses default charset]
Forbidden class/interface use: java.io.FileReader [Uses default charset]
Forbidden method invocation: java.text.SimpleDateFormat#(java.lang.String) [Uses default locale]
Forbidden method invocation: java.text.MessageFormat#format(java.lang.String,java.lang.Object[]) [Uses default locale]

Solution:
As I mentioned, do not use default Locale, character sets etc. However, which Locale should we use if we won’t use default Locale. Using Locale.ENGLISH may seem a solution for it but what if English character set changes in the future? We should find a generic solution for it as like the approach we develop software. There is a solution for it: using Locale.ROOT. These are examples of fixing such errors:

-  return MessageFormat.format("Stopping in {0} seconds.", DELAY_SEC);
+  return new MessageFormat("Stopping in {0} seconds.", Locale.ROOT).format(DELAY_SEC);

-  return new FileWriter(seedFile);
+  return new OutputStreamWriter(new FileOutputStream(seedFile), StandardCharsets.UTF_8));

-  SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+  SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ROOT);

-  value.getBytes();
+  value.getBytes(StandardCharsets.UTF_8);

-  new FileReader(arg[0]);
+  new InputStreamReader(new FileInputStream(arg[0]), StandardCharsets.UTF_8));

-  strValue.toLowerCase();
+  strValue.toLowerCase(Locale.ROOT);

-  DateFormat df = DateFormat.getDateTimeInstance();
+  DateFormat df = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM, Locale.ROOT);

Conclusion:
As a result, you should not trust default Locale and character sets to avoid such problems. You should find and fix such API calls.

kamaci

Leave a Reply

Your email address will not be published. Required fields are marked *