[ACCEPTED]-Avoiding multiple If statements in Java-java

Accepted answer
Score: 35

You can use a Map to hold your solutions:

Map<String,String> extensionToMimeType = new HashMap<String,String>();
extensionToMimeType.put("pdf", "application/pdf");
extensionToMimeType.put("doc", "application/msword");
// and the rest

int lastDot = fileName.lastIndexOf(".");
String mimeType;
if (lastDot == -1) {
    mimeType = NO_EXTENSION_MIME_TYPE;
} else {
    String extension = fileName.substring(lastDot+1);
    mimeType = extensionToMimeType.getOrDefault(extension, 
                                                UNKNOWN_EXTENSION_MIME_TYPE);
}

For 2 this code to work you'll need to have defined 1 NO_EXTENSION_MIME_TYPE and UNKNOWN_EXTENSION_MIME_TYPE as in your class, somewhat like this:

private static final String NO_EXTENSION_MIME_TYPE = "application/octet-stream";
private static final String UNKNOWN_EXTENSION_MIME_TYPE = "text/plain";
Score: 11

Using a HashMap perhaps?

This way you could do myMap.get(mystr);

0

Score: 5

Command pattern is the way to go. Here is 1 one example using java 8:

1. Define the interface:

public interface ExtensionHandler {
  boolean isMatched(String fileName);
  String handle(String fileName);
}

2. Implement the interface with each of the extension:

public class PdfHandler implements ExtensionHandler {
  @Override
  public boolean isMatched(String fileName) {
    return fileName.endsWith(".pdf");
  }

  @Override
  public String handle(String fileName) {
    return "application/pdf";
  }
}

and

public class TxtHandler implements ExtensionHandler {
  @Override public boolean isMatched(String fileName) {
    return fileName.endsWith(".txt");
  }

  @Override public String handle(String fileName) {
    return "txt/plain";
  }
}

and so on .....

3. Define the Client:

public class MimeTypeGetter {
  private List<ExtensionHandler> extensionHandlers;
  private ExtensionHandler plainTextHandler;

  public MimeTypeGetter() {
    extensionHandlers = new ArrayList<>();

    extensionHandlers.add(new PdfHandler());
    extensionHandlers.add(new DocHandler());
    extensionHandlers.add(new XlsHandler());

    // and so on

    plainTextHandler = new PlainTextHandler();
    extensionHandlers.add(plainTextHandler);
  }

  public String getMimeType(String fileExtension) {
    return extensionHandlers.stream()
      .filter(handler -> handler.isMatched(fileExtension))
      .findFirst()
      .orElse(plainTextHandler)
      .handle(fileExtension);
  }
}

4. And this is the sample result:

  public static void main(String[] args) {
    MimeTypeGetter mimeTypeGetter = new MimeTypeGetter();

    System.out.println(mimeTypeGetter.getMimeType("test.pdf")); // application/pdf
    System.out.println(mimeTypeGetter.getMimeType("hello.txt")); // txt/plain
    System.out.println(mimeTypeGetter.getMimeType("my presentation.ppt")); // "application/vnd.ms-powerpoint"
  }
Score: 4

Personally I don't have problems with the 15 if statements. The code is readable, it 14 took just milliseconds to understand what 13 you're doing. It's a private method anyway 12 and if the list of mime types is static 11 then there's no urgent need to move the 10 mapping to a properties file and use a lookup 9 table (map). Map would reduce lines of code, but 8 to understand the code, then you're forced 7 to read the code and the implementation 6 of the mapping - either a static initializer 5 or an external file.

You could change the code 4 a bit and use an enum:

private enum FileExtension { NONE, DEFAULT, PDF, DOC, XLS /* ... */ }

private String getMimeType(String fileName){
  String mimeType = null;

  FileExtension fileNameExtension = getFileNameExtension(fileName);

  switch(fileNameExtension) {
    case NONE:
      return "";
    case PDF:
      return "application/pdf";

    // ...

    case DEFAULT:
      return "txt/plain";   
  }

  throw new RuntimeException("Unhandled FileExtension detected");
} 

The getFileNameExtension(String fileName) method will just 3 return the fitting enum value for the fileName, FileExtension.NONE if 2 fileName is empty (or null?) and FileExtension.DEFAULT if the 1 file extension is not mapped to a mime type.

Score: 4

what about using a MIME detection library instead?

  • mime-util
  • mime4j
  • JMimeMagic library - Free. Uses file extension and magic headers to determine MIME type.
  • mime-util - Free. Uses file extension and magic headers to determine MIME type.
  • DROID (Digital Record Object Identification) - Free. Uses batch automation to detect MIME types.
  • Aperture Framework - Free. A framework for crawling external sources to identify MIME types.

(feel free to 1 add more, there so many libraries..)

Score: 2

I consider your approach to be the best 7 overall. This comes after having tested 6 with a number of different approaches myself.

I 5 see a number of huge benefits in your current 4 approach, namely:

  1. Easily readable and understandable by anyone (in my experience, medium-level programmers often underestimate this and usually prefer going with fancy-patterns which, in the end are not readable at all for the vast majority of programmers who do not know that specific pattern)
  2. All the information is in one single place. As Andreas_D pointed out, hunting around files or classes is not a good option for someone that needs to fix a bug while you are on holiday!
  3. Easily maintainable: I could "F3" (if you are Eclipse-ing) on the method and add a new content type in seconds without any worries of introducing bugs!

I can suggest a few things 3 anyway:

  1. This method is very general purpose: Why should it be private?! This is a public method of some utility/helper class! Moreover it should be a static method!! You don't need anything from the Object itself to perform your job!
  2. You could use indenting to make things prettier and compact. I know that indenting is some kind of religion for the most of us, but I think it should not be a strict rule; it should be properly used to make our code more readable and compact. If this would be a config file you would probably have something like:
pdf=application/pdf
doc=application/msword

You could have a very similar result 2 with:

    public static String getMimeType(String fileName){
       if(fileName == null) return "";
       if(fileName.endsWith(".pdf")) return "application/pdf";
       if(fileName.endsWith(".doc")) return "application/msword";
       if(fileName.endsWith(".xls")) return "application/vnd.ms-excel"; 
       return "txt/plain"; 
   }

This is also what a lot of the Map 1 based implementations look like.

Score: 1

There is no way to evade that in general. In 4 your case - if there is a set of allowed 3 extensions - you could create an Enum, convert 2 the extension to the Enum type via valueOf(), and 1 then you can switch over your enum.

Score: 1

I would do this by putting the associations 9 in a map, and then using the map for lookup:

Map<String, String> map = new HashMap<String, String>();

map.put(".pdf", "application/pdf");
map.put(".doc", "application/msword");
// ... etc.

// For lookup:
private String getMimeType(String fileName) {
    if (fileName == null || fileName.length() < 4) {
        return null;
    }

    return map.get(fileName.substring(fileName.length() - 4));
}

Note 8 that using the switch statements on strings is 7 one of the proposed new features for the 6 next version of Java; see this page for more details 5 and an example of how that would look in 4 Java 7:

switch (fileName.substring(fileName.length() - 4)) {
    case ".pdf": return "application/pdf";
    case ".doc": return "application/msword";
    // ...
    default: return null;

(edit: My solution assumes the file 3 extension is always 3 letters; you'd have 2 to change it slightly if it can be longer 1 or shorter).

Score: 1

Easiest and shortest way for this particular 6 problem would be using the builtin Java 5 SE or EE methods.

Either in "plain vanilla" client 4 application (which derives this information 3 from the underlying platform):

String mimeType = URLConnection.guessContentTypeFromName(filename);

Or in a JSP/Servlet 2 web application (which derives this information 1 from the web.xml files):

String mimeType = getServletContext().getMimeType(filename);
Score: 0

You can always use a Groovy class here as 1 it allows for switch-case on Strings :)

Score: 0

How about mapping the extensions to MIME 1 types, then using a loop? Something like:

Map<String,String> suffixMappings = new HashMap<String,String>();
suffixMappings.put(".pdf", "application/pdf");
...

private String getMimeType(String fileName){
    if (fileName == null) {
        return "";   
    }
    String suffix = fileName.substring(fileName.lastIndexOf('.'));
    // If fileName might not have extension, check for that above!
    String mimeType = suffixMappings.get(suffix); 
    return mimeType == null ? "text/plain" : mimeType;
 } 
Score: 0

Create an enum called MimeType with 2 String 5 variables: extension and type. Create an 4 appropriate constructor and pass in the 3 ".xxx" and the "application/xxx" values. Create 2 a method to do the lookup. You can use enums 1 in switch.

Score: 0

Just to mention it: A direct equivalent 14 to your code would not be using a map for 13 direct lookup (since that would require 12 each extension to have exactly 3 characters) but 11 a for loop:

...
Map<String, String> extmap = GetExtensionMap();
for (Map.Entry<String,String> entry: extmap.entrySet())
  if (fileName.endsWith(entry.getKey))
    return entry.getValue();
...

This solution works with extensions 10 of any length but is less performant than 9 the hash lookup of course (and slightly 8 less performant than the original solution)

The Algorithmic-Design-Guy solution

A 7 more performant way would be to implement 6 a tree structure starting with the last character 5 of the extension and storing the appropriate 4 MIME types at the respective nodes. You 3 could then walk down the tree starting with 2 the last character of the file name. But 1 this is probably an overkill ...

More Related questions