Friday, October 17, 2008

Multi-line String Literals in Java

I've implemented multi-line strings for Java using a library approach. The following code:

 public static void main(String[] args) {
   System.out.println(S(/*
      Wow, we finally have
      multiline strings in
      Java! HOOO!
    */));
 }
will print the following to standard out:
       Wow, we finally have
       multiline strings in
       Java! HOOO!


How does this work?
Well, first of all you have to make sure that your source is on the class path, then the following code does the job:
  public static String S() {
    StackTraceElement element = new RuntimeException().getStackTrace()[1];
    String name = element.getClassName().replace('.', '/') + ".java";
    InputStream in = getClassLoader().getResourceAsStream(name);
    String s = convertStreamToString(in, element.getLineNumber());
    return s.substring(s.indexOf("/*")+2, s.indexOf("*/"));
  }

Obviously this doesn't perform that well, but for unit testing it's sufficient.
Maybe it would be cool to add some support for interpolation functionality to it (Edit : or just use Xtend).

13 comments:

  1. This would be an improvement for defining regular expressions with lots of backspaces, too! Writing ^("|')(.):([\n\d\\]*)(\1)$ in Java where quotes and backslashes have to be escaped is a nightmare.

    ReplyDelete
  2. @Heiko:
    try quickrex. It has a feature to quote regex for use in a java string.

    ReplyDelete
  3. I like your spirit, still is a bit of hack though. I think it would be best to just write in a JVM language that allows for multiline comments, like my favorite for instance, Groovy.

    ReplyDelete
  4. Smart and efficient way to work around an obvious lack of multi line string definition. Certainly very useful in the context of a Unit test which is my case right now.

    ReplyDelete
  5. I demand the right to choose whether I have to externalize strings (like blocks of sql) from my code or not. Personally, if things are all in one place ie. inline, my brain has to jump through less hoops.

    If the java language guys don't get it, well we'll just have to keep hacking won't we?

    Nice bit of code!

    ReplyDelete
  6. This is an awesome hack! Mad props. Reflection is king :).

    ReplyDelete
  7. Looks Interesting

    But where do you get getClassLoader() and convertStreamToString from?

    ReplyDelete
  8. Hey, yeah, thats a nice hack. but i must follow jerzy.orlowski. cannot find getClassLoader() and convertStreamToString in the javadocs. all that google spits out is some custom code.

    i also found a presentation of you, showing the exact same code along with some other snippets.

    But now i'm asking you: what are your snippets worth when they include some insider-code that is not directly provided on the webpage?

    Could you please provide more details on that issue?

    greetings,
    kdot

    ReplyDelete
  9. @ Jerzy and Kdot.

    getClassLoader can be implmented arbitrary. The most simple impl would be

    private ClassLoader getClassLoader() {
    return getClass().getClassLoader();
    }

    the convertStreamToString method does what the method's name suggests: It excepts an inputstream and gives you the String of its contents. Please use google to find out how to do this, as it is not subject of the blog entry.

    ReplyDelete
  10. getClass().getClassLoader() does not work as expected if this code is in a dependent jar. This will only find the classloader of the jar/class this is written in. If you have this code in a jar referenced by another jar/war, this will not find classpath entries of parent. It will only find classpath entries within the original jar. In order to access the classpath of the application, including all dependencies, you should use Thread.currentThread().getContextClassLoader() . In addition to making the entire application's classpath available, this gives you the order of loading the classpath in the same order the application uses for everything.
    For instance, Let's say you have a jar called mycommon.jar and in the root of that jar is a config.xml. Let's say you also have web project that depends on mycommon.jar but the web project has a different config.xml. Using the thread access to the classloader ensures that you would pick up the one in your web project before it finds the one in your jar, which is usually desired behavoir.

    ReplyDelete
  11. @solid: this is not true if you run a vanilla Java process. Also note that if you run your application within an OSGi container using the Thread's context classloader is a bad idea. But you are right for a JEE environment.

    ReplyDelete
  12. Performance will suffer so much...

    ReplyDelete