Friday, November 25, 2011

File Management in Java with Guava's Files Class

Both Groovy and Java SE 7 provide improvements for file management in Java as I discussed in posts here, here, here, and here. However, when a particular Java application is not able to yet use Java SE 7 or Groovy for its file management, an improved file management experience can still be obtained by using Guava's Files class.

File handling has always seemed a little more difficult in Java than in many other programming languages. Java SE 7 certainly improves Java's file handling capabilities dramatically with NIO.2, but not every project can make use of Java SE 7. For those projects not able to use Java SE 7, Guava's Files class is a nice intermediate solution for easier file handling. It is important to note here that Java SE 7 introduces a Files class of its own, so any use of Guava's Files class in Java SE 7 must be fully scoped if the Java version of Files is used in the same code. My examples have been written and built in Java SE 7, but I avoid using Java SE 7's Files class and so don't need to fully scope Guava's same-named class.

File Creation

Guava's Files class includes a couple overloaded write methods for easily writing content to a file. The next code sample demonstrates using Files.write(byte[],File).

Demonstrating Files.write(byte[],File)
   /**
    * Demonstrate writing bytes to a specified file.
    * 
    * @param fileName Name of file to be written to.
    * @param contents Contents to be written to file.
    */
   public void demoFileWrite(final String fileName, final String contents)
   {
      checkNotNull(fileName, "Provided file name for writing must NOT be null.");
      checkNotNull(contents, "Unable to write null contents.");
      final File newFile = new File(fileName);
      try
      {
         Files.write(contents.getBytes(), newFile);
      }
      catch (IOException fileIoEx)
      {
         err.println(  "ERROR trying to write to file '" + fileName + "' - "
                     + fileIoEx.toString());
      }
   }

There are a couple observations that can be from this first code sample that will apply to all other code samples in this post. First, I make use of the statically imported Guava Preconditions class to provide an easy check ensuring that the provided parameters are not null. The second common feature of this code is that it explicitly catches and "handles" the checked exception IOException. All other Files methods demonstrated in this post similarly throw this same checked exception.

File Copying

The next example demonstrates how easy it is to copy files using Guava's Files.copy(File,File) method (one of several overloaded methods with name 'copy').

Demonstrating Files.copy(File,File)
   /**
    * Demonstrate simple file copying in Guava. This demonstrates one of the
    * numerous overloaded copy methods provided by Guava's Files class. The
    * version demonstrated here copies one provided File instance to another
    * provided File instance.
    * 
    * @param sourceFileName Name of file that is to be copied.
    * @param targetFileName Name of file that is result of file copying.
    */
   public void demoSimpleFileCopy(
      final String sourceFileName, final String targetFileName)
   {
      checkNotNull(sourceFileName, "Copy source file name must NOT be null.");
      checkNotNull(targetFileName, "Copy target file name must NOT be null.");
      final File sourceFile = new File(sourceFileName);
      final File targetFile = new File(targetFileName);
      try
      {
         Files.copy(sourceFile, targetFile);
      }
      catch (IOException fileIoEx)
      {
         err.println(
              "ERROR trying to copy file '" + sourceFileName
            + "' to file '" + targetFileName + "' - " + fileIoEx.toString());
      }
   }
File Moving

Moving files with Guava is as easy as copying. The Files.move(File,File) method is demonstrated in the next code snippet.

Demonstrating Files.move(File,File)
  /**
    * Demonstrate moving a file with Guava's Files.move(File,File).
    * 
    * @param sourceFileName Path/name of File to be moved.
    * @param targetFileName Path/name of Destination of file.
    */
   public void demoMove(final String sourceFileName, final String targetFileName)
   {
      checkNotNull(sourceFileName, "Move source file name must NOT be null.");
      checkNotNull(targetFileName, "Move destination name must NOT be null.");
      final File sourceFile = new File(sourceFileName);
      final File targetFile = new File(targetFileName);
      try
      {
         Files.move(sourceFile, targetFile);
      }
      catch (IOException fileIoEx)
      {
         err.println(
              "ERROR trying to move file '" + sourceFileName
            + "' to '" + targetFileName + "' - " + fileIoEx.toString());
      }
   }
Comparing Files

Determining if two files are the same is straightforward with the Gauva Files.equal(File,File) method.

Demonstrating Files.equal(File,File)
   /**
    * Demonstrate using Guava's Files.equal(File,File) to compare contents of
    * two files.
    * 
    * @param fileName1 Name of first file to be compared.
    * @param fileName2 Name of second file to be compared.
    */
   public void demoEqual(final String fileName1, final String fileName2)
   {
      checkNotNull(fileName1, "First file name for comparison must NOT be null.");
      checkNotNull(fileName2, "Second file name for comparison must NOT be null.");
      final File file1 = new File(fileName1);
      final File file2 = new File(fileName2);
      try
      {
         out.println(
             "File '" + fileName1 + "' "
           + (Files.equal(file1, file2) ? "IS" : "is NOT")
           + " the same as file '" + fileName2 + "'.");
      }
      catch (IOException fileIoEx)
      {
         err.println(
              "ERROR trying to compare two files '"
            + fileName1 + "' and '" + fileName2 + "' - " + fileIoEx.toString());
      }
   }
Touching Files

Touching a file to create a new empty file or to update the timestamp on an existing file can be easily accomplished with Guava's Files.touch(File) as shown in the next code sample.

Demonstrating Files.touch(File)
   /**
    * Demonstrate Guava's Files.touch(File) method.
    * 
    * @param fileNameToBeTouched Name of file to be 'touch'-ed.
    */
   public void demoTouch(final String fileNameToBeTouched)
   {
      checkNotNull(fileNameToBeTouched, "Unable to 'touch' a null filename.");
      final File fileToTouch = new File(fileNameToBeTouched);
      try
      {
         Files.touch(fileToTouch);
      }
      catch (IOException fileIoEx)
      {
         err.println(
              "ERROR trying to touch file '" + fileNameToBeTouched
            + "' - " + fileIoEx.toString());
      }
   }
Retrieving File Contents

With a simplicity reminiscent of Groovy's GDK extension File.getText(), Guava's Files.toString(File,Charset) makes it easy to retrieve text contents of a file.

Demonstrating Files.toString(File,Charset)
   /**
    * Demonstrate retrieving text contents of a specified file with Guava's 
    * Files.toString(File) method.
    * 
    * @param nameOfFileToGetTextFrom Name of file from which text is to be
    *    retrieved.
    */
   public void demoToString(final String nameOfFileToGetTextFrom)
   {
      checkNotNull(nameOfFileToGetTextFrom, "Unable to retrieve text from null.");
      final File sourceFile = new File(nameOfFileToGetTextFrom);
      try
      {
         final String fileContents = Files.toString(sourceFile, Charset.defaultCharset());
         out.println(
              "Contents of File '" + nameOfFileToGetTextFrom
            + "' are: " + fileContents);
      }
      catch (IOException fileIoEx)
      {
         err.println(
              "ERROR trying to get text contents of file '"
            + nameOfFileToGetTextFrom + "' - " + fileIoEx.toString());
      }
   }
Temporary Directory Creation

Guava makes it easy to generate a temporary directory with Files.createTempDir().

Demonstrating Files.createTempDir()
   /**
    * Demonstrate Guava's Files.createTempDir() method for creating a temporary
    * directory.
    */
   public void demoTemporaryDirectoryCreation()
   {
      final File newTempDir = Files.createTempDir();
      try
      {
         out.println(
            "New temporary directory is '" + newTempDir.getCanonicalPath() + "'.");
      }
      catch (IOException ioEx)
      {
         err.println("ERROR: Unable to create temporary directory - " + ioEx.toString());
      }
   }

I don't provide a code sample here, but it's worth noting that Guava provides a convenience method for creating a new directory will all necessary new parent directories with its Files.createParentDirs(File) method.

Retrieving Contents of File as Lines

There are times when it is most convenient to get the contents of a file as a series of lines so that each line can be processed. This is commonly done in Groovy with overloaded versions of readLines() and eachLine(). Guava provides similar functionality to Groovy's File.readLines() with its Files.readLines(File, Charset) method. This is demonstrated in the following code sample.

Demonstrating Files.readLines(File,Charset)
   /**
    * Demonstrate extracting lines from file.
    * 
    * @param fileName Name of file from which lines are desired.
    */
   public void demoRetrievingLinesFromFile(final String fileName)
   {
      final File file = new File(fileName);
      try
      {
         final List<String> lines = Files.readLines(file, Charset.defaultCharset());
         for (final String line : lines)
         {
            out.println(">> " + line);
         }
      }
      catch (IOException ioEx)
      {
         err.println(
              "ERROR trying to retrieve lines from file '"
            + fileName + "' - " + ioEx.toString());
      }
   }

The other overloaded version of readLines is interesting because it allows a LineProcessor callback to be specified to terminate returning of lines earlier than the end of the file.

Reading File's First Line

I have run into numerous situations in which it has been useful to read only the first line of file. This first line might tell my code what type of script is being run, provide XML prolog information, or other interesting overview data of a file's contents. Guava makes it easy to retrieve just the first line with the Files.readFirstLine(File,Charset) method. This is demonstrated in the next code listing.

Demonstrating Files.readFirstLine(File,Charset)
   /**
    * Demonstrate extracting first line of file.
    * 
    * @param fileName File from which first line is to be extracted.
    */
   public void demoRetrievingFirstLineFromFile(final String fileName)
   {
      final File file = new File(fileName);
      try
      {
         final String line = Files.readFirstLine(file, Charset.defaultCharset());
         out.println("First line of '" + fileName + "' is '" + line + "'.");
      }
      catch (IOException fileIoEx)
      {
         err.println(
              "ERROR trying to retrieve first line of file '"
            + fileName + "' - " + fileIoEx.toString());
      }
   }
There's Much More

Although I have discussed and demonstrated several useful Files methods in this post, the class has many more to offer. Some of these include the ability to append to an existing file with Files.append(CharSequence,File,Charset), getting a file's checksum with Files.getChecksum(File,Checksum), getting a file's digest with Files.getDigest(File,MessageDigest), accessing a BufferedReader with Files.newReader(File,Charset), accessing a BufferedWriter with Files.newWriter(File,Charset), and accessing a MappedByteBuffer mapped to an underlying file via overloaded Files.map methods.

Conclusion

File handling in Java is much easier and more convenient with Guava's Files class. Guava brings file-handling convenience to Java applications that cannot make use of Groovy's or Java SE 7's file handling conveniences.

Thursday, November 24, 2011

The Thankful Software Developer

Neil McAllister's Fatal Exception blog post What I'm thankful for as a developer is timely given the Thanksgiving holiday weekend in the United States. In that post, McAllister expresses gratitude for open source tools, online documentation and support, modern IDEs, desktop virtualization, distributed version control, and jQuery. I use the remainder of this post to look at some of the thing I'm thankful for as a developer.

Open Source Tools, Libraries, Frameworks, and More

Like McAllister and doubtless thousands or even millions of other developers, I'm thankful for open source tools and online documentation and support. I've been the beneficiary of others' work in the open source community with products (tools, libraries, frameworks, etc.) such as Struts, Java, JFreeChart, Flex, OpenLaszlo, Groovy, Ruby on Rails, Spring Framework, Guava, Apache Commons, TopLink Essentials, EclipseLink, Ant, JUnit, NetBeans, Eclipse, GlassFish, Apache POI, Apache ActiveMQ, Apache Batik, Apache FOP, and numerous others.

Online Documentation and Resources

I also agree with McAllister that one of the things most developers should be thankful for is online documentation and resources such as articles, blogs, and even electronic books. Each of these types of online resources has its own set of advantages. I covered these and their advantages in the post Useful Online Java Developer Resources. Portions of that post are Java-specific, but many apply to any programming language. As I stated in that post, I am appreciative of content aggregation sites such as DZone, Reddit (programming and java), and Java.net. I am similarly appreciative of focused news sites such as JavaWorld. I similarly appreciate online documentation that vendors make available.

The Internet was hitting mainstream about the same time that I started my professional career in software development. Although it was available from the beginning of my career, it took a couple years to catch on and I think there are a couple of reasons for this. First, the percentage of people putting content on the web was much lower then. Most pages were static on sites such as GeoCities. Although not terribly difficult to use, these sites were just difficult enough to prevent a large percentage of developers from creating web sites and documenting their experiences. Blogging and so-called micro-blogging have changed all that. The second major development leading to increased usefulness of online resources in software development is today's powerful search engines. The Google search engine, as discussed in my post Useful Online Java Developer Resources, is still my favorite. The combination of availability of resources and the ability to quickly and efficiently find relevant resources have made online resources invaluable to software developers.

Community and Lessons Learned

One can learn valuable lessons from hard experience. It's even better, though, to learn from others' hard experiences. I have come to value learning from others' experiences and their lessons learned. There is a wealth of knowledge and insight out there. This is one of the reasons that I've advocated more developers writing blogs.

Contrarian Opinions

I continue to value contrarian opinions in software development. I don't always agree with them, but I appreciate people who challenge potential herd mentality. They may be wrong in the end, but the challenge helps me and others to think through the merits and drawbacks of any particular approach, language, toolkit, framework, or methodology. The Spring Framework arose from someone recognizing flaws with EJBs in 1.x and 2.x versions. Younger developers today may take it for granted that people think badly of EJB 1.x and EJB 2.x, but I remember a lot of Kool-aid drinking going on at the time as "experts" told us what was good for us. Given this, I am appreciate of posts such as Stephen Colebourne's recent Scala feels like EJB 2, and other thoughts and follow-up Scala EJB 2 Follow-up. I don't know enough about Scala to take sides, but it seems nice to see an opinion of Scala that doesn't imply that Scala will solve world hunger.

Modern Language Features

I am thankful to have so many new language features at my disposal. For example, garbage collection has been a tremendous boon to productivity. Similarly, I look forward to improvements in Java such as better modularity (fewer class loader and classpath issues) and lambda expressions.

Choice

I'm a strong believer in using the correct tool for the job and, in most cases, no one tool covers every job as well as I'd like. Therefore I appreciate having a wide variety of languages, tools, and frameworks to choose from. I particularly enjoy the recent surge in alternative JVM languages. I use Java for most production tasks, but use other languages (especially Groovy) where they are a better fit.

The Joy of Software Development

In his famous book The Mythical Man-Month, Frederick P. Brooks, Jr., wrote in the 25th Anniversary Edition prologue, "Too many interests, too many exciting opportunities for learning, research, and thought. What a marvelous predicament! Not only is the end not in sight, the pace is not slackening. We have many future joys." Brooks wrote earlier in the book, "Programming then is fun because it gratifies creative longings built deep within us and delights sensibilities we have in common with all men." Brooks also stated, "To only a fraction of the human race does God give the privilege of earning one's own bread doing what one would have gladly pursued free, for passion. I am very thankful." This is what we have to be most thankful for in software development: there are so many new things to learn and try and play with. I am grateful to have found a career in which I can make a living and enjoy what I do.

Monday, November 21, 2011

Two Generally Useful Guava Annotations

Guava currently (Release 10) includes four annotations in its com.google.common.annotations package: Beta, VisibleForTesting, GwtCompatible, and GwtIncompatible. The last two are specific to use with Google Web Toolkit (GWT), but the former two can be useful in a more general context.

The @Beta annotation is used within Guava's own code base to indicate "that a public API (public class, method or field) is subject to incompatible changes, or even removal, in a future release." Although this annotation is used to indicate at-risk public API constructs in Guava, it can also be used in code that has access to Guava on its classpath. Developers can use this annotation to advertise their own at-risk public API constructs.

The @Beta annotation is defined as a @Documented, which means that it marks something that is part of the public API and should be considered by Javadoc and other source code documentation tools.

The @VisibleForTesting annotation "indicates that the visibility of a type or member has been relaxed to make the code testable." I have never liked having to relax type or member visibility to make something testable. It feels wrong to have to compromise one's design to allow testing to occur. This annotation is better than nothing in such a case because it at least makes it clear to others using the construct that there is a reason for its otherwise surprisingly relaxed visibility.

Conclusion

Guava provides two annotations that are not part of the standard Java distribution, but cover situations that we often run into during Java development. The @Beta annotation indicates a construct in a public API that may be changed or removed. The @VisibleForTesting annotation advertises to other developers (or reminds the code's author) when a decision was made for relaxed visibility to make testing possible or easier.

Monday, November 14, 2011

Effective Javadoc Documentation Illustrated in Familiar Projects

Three years ago, I wrote about practices that I believe lead to more effective Javadoc in my post More Effective Javadoc. In this post, I look at some familiar projects which provide good examples of effective Javadoc documentation practices. I, of course, will only be covering a very tiny representative sample of the many good projects and many good Javadoc ideas that are out there.

1. Advertising Ultimate Demise of Deprecated Method (Guava)

The current version of Guava (Release 10) provides some good examples of more informative statements of deprecation. The next screen snapshot shows the @deprecated text for methods Files.deleteDirectoryContents(File) and Files.deleteRecursively(File). In both methods' cases, the documentation states why the method is deprecated and, most refreshingly, states when it is envisioned that the method will be removed (Release 11 in these cases). I like the idea of stating in the deprecation statement when the deprecated thing is going away. It is easy to learn to ignore @deprecated and @Deprecated if one believes they are really never going to go away. Stating a planned removal version or date implies more urgency in not using deprecated features and provides fair warning to users.

Although the source code for each of these methods employs the @Deprecated annotation, the code from both cases does not specify this text with Javadoc's @deprecated, but instead simply specifies the deprecation details as part of the normal method description text with bold tags around the word "Deprecated." I'm not sure why this was done instead of using the Javadoc tag explicitly intended for this purpose.

2. Documenting Use of an API (Java SE, Java EE, Guava, Joda Time)

When learning how to use a new API, it is helpful when the Javadoc documentation provides examples of using that API. I first learned how to marshal and unmarshal JAXB objects by reading the Javadoc documentation for Marshaller and Unmarshaller respectively. Both of these classes take advantage of class-level documentation to describe how to use the class's APIs.

Guava's class-level description for Stopwatch shows how to use most of that class's features in a concise and easily understandable class usage description.

Use of an API can be documented at the method level as well as at the class level. Examples of this are Guava's Throwables.propagateIfInstanceOf method and the overloaded Throwables.propagateIfPossible methods . The Javadoc documentation for these methods shows "example usage" for each.

API documentation is not limited to the class level or method level. The javax.management package-level documentation provides a nice overview of Java Management Extensions (JMX). The first sentence of the package description (which is what's always shown at top) is simple enough: "Provides the core classes for the Java Management Extensions." However, there are far more details in the rest of the package description. The next screen snapshot shows a small portion of that package documentation.

Another example of a useful package-level description is the package description for Joda Time package org.joda.time. This core package describes many of the concepts applicable to the entire project in one location.

3. Explicitly Declaring Throws Clause for Unchecked Exceptions (Guava)

In my post More Effective Javadoc, I stated that it is best to "document all thrown exceptions" whether they are checked or unchecked. Guava's InetAddresses.forString(String) method's documentation does this, specifying that it throws the runtime exception IllegalArgumentException.

4. Using -linksource (JFreeChart, Guava)

For an open source project, a nice benefit that can be provided to developers using that project is to allow linking of Javadoc documentation to underlying source code. The next screen snapshots indicate this for JFreeChart and Guava. There are two images for each project, with the first image showing the Javadoc with link to source code annotated and the second image showing the source code displayed when the class name is clicked on in the Javadoc.

It is very convenient to be able to move easily between the Javadoc documentation and the source code. Of course, this can also be done in an IDE that supports Javadoc presentation in conjunction with code.

Conclusion

This post has highlighted several familiar projects who Javadoc documentation provides examples of more effective Javadoc-based documentation.

Thursday, November 10, 2011

Speaking at RMOUG Training Days 2012

The Rocky Mountain Oracle Users Group (RMOUG) has announced that the keynote speaker at RMOUG Training Days 2012 will be Cary Millsap of Method R Consulting. RMOUG Training Days 2012 are scheduled for 14-16 February 2012 at the Colorado Convention Center in Denver, Colorado.

I will be speaking at this conference. I will be sole presenter of "JavaFX 2.0: Java in the Rich Internet Application Space" and will be a co-presenter of "Things Developers Wish Managers Knew and Managers Wish Developers Knew." I look forward to returning to the Colorado Convention Center for another edition of RMOUG Training Days.

Saturday, November 5, 2011

Apache Harmony Retiring to the Attic

In a development that seemed destined to happen since IBM's joining OpenJDK, Apache Harmony's Project Management Committee (PMC) has voted 18-2 to move Apache Harmony to the Apache Attic. The Apache Attic page explains the purpose of the Apache Attic: "The Apache Attic was created in November 2008 to provide process and solutions to make it clear when an Apache project has reached its end of life." In short, it has been determined that Apache Harmony has reached its end of life.

Although Apache Harmony enjoyed widespread popularity among numerous Java developers hoping for an open source implementation of standard Java, it was never able to garner support from Sun or Oracle to provide an "independent, compatible implementation of Java SE." With main supporter IBM moving to OpenJDK, this latest development is not too surprising.

Not all projects die once they hit the attic and some live on after retirement. iBatis, for example, lives on as MyBatis (hosted on Google Code). This project "forking" is an example of one of the three approaches for "leaving the attic again." The other two ways of leaving the attic are to return to the Apache Incubator or to recreate a PMC for the project. At this point, my best guess is that none of the three approaches will be used. There is talk on the mailing list of moving it to the public domain, but that also appears unlikely given licensing issues. There has also been hope of Google forking it, but that may be less appealing to Google given the current lawsuit over Android.

With the retirement of Apache Harmony, OpenJDK becomes the sole large open source implementation of Java SE. With the support of Oracle, IBM, Apple, and Twitter (among others), OpenJDK has definitely had the inside track to this position.

Wednesday, November 2, 2011

Guava's Strings Class

In the post Checking for Null or Empty or White Space Only String in Java, I demonstrated common approaches in the Java ecosystem (standard Java, Guava, Apache Commons Lang, and Groovy) for checking whether a String is null, empty, or white space only similar to what C# supports with the String.IsNullOrWhiteSpace method. One of the approaches I showed was a Guava-based approach that made use of the Guava class Strings and its static isNullOrEmpty(String) method. In this post, I look at other useful functionality for working with Strings that is provided by Guava's six "static utility methods pertaining to String" that are bundled into the Strings class.

Using Guava's Strings class is straightforward because of its well-named methods. The following list enumerates the methods (all static) on the Strings class with a brief description of what each does next to the method name (these descriptions are borrowed or adapted from the Javadoc documentation).

isNullOrEmpty

Guava's Strings.isEmptyOrNull(String) method makes it easy to build simple and highly readable conditional statements that check a String for null or emptiness before acting upon said String. As previously mentioned, I have briefly covered this method before. Another code demonstration of this method is shown next.

Code Sample Using Strings.isNullOrEmpty(String)
   /**
    * Print to standard output a string message indicating whether the provided
    * String is null or empty or not null or empty. This method uses Guava's
    * Strings.isNullOrEmpty(String) method.
    * 
    * @param string String to be tested for null or empty.
    */
   private static void printStringStatusNullOrEmpty(final String string)
   {
      out.println(  "String '" + string + "' "
                  + (Strings.isNullOrEmpty(string) ? "IS" : "is NOT")
                  + " null or empty.");
   }

   /**
    * Demonstrate Guava Strings.isNullOrEmpty(String) method on some example
    * Strings.
    */
   public static void demoIsNullOrEmpty()
   {
      printHeader("Strings.isNullOrEmpty(String)");
      printStringStatusNullOrEmpty("Dustin");
      printStringStatusNullOrEmpty(null);
      printStringStatusNullOrEmpty("");
   }

The output from running the above code is contained in the next screen snapshot. It shows that true is returned when either null or empty String ("") is passed to Strings.isNullOrEmpty(String).

nullToEmpty and emptyToNull

There are multiple times when one may want to treat a null String as an empty String or wants present a null when an empty String exists. In cases such as these when transformations between null and empty String are desired, The following code snippets demonstrate use of Strings.nullToEmpty(String) and Strings.emptyToNull(String).

nullToEmpty and emptyToNull
   /**
    * Print to standard output a simple message indicating the provided original
    * String and the provided result/output String.
    * 
    * @param originalString Original String.
    * @param resultString Output or result String created by operation.
    * @param operation The operation that acted upon the originalString to create
    *    the resultString.
    */
   private static void printOriginalAndResultStrings(
      final String originalString, final String resultString, final String operation)
   {
      out.println("Passing '" + originalString + "' to " + operation + " produces '" + resultString + "'");
   }

   /** Demonstrate Guava Strings.emptyToNull() method on example Strings. */
   public static void demoEmptyToNull()
   {
      final String operation = "Strings.emptyToNull(String)";
      printHeader(operation);
      printOriginalAndResultStrings("Dustin", Strings.emptyToNull("Dustin"), operation);
      printOriginalAndResultStrings(null, Strings.emptyToNull(null), operation);
      printOriginalAndResultStrings("", Strings.emptyToNull(""), operation);
   }

   /** Demonstrate Guava Strings.nullToEmpty() method on example Strings. */
   public static void demoNullToEmpty()
   {
      final String operation = "Strings.nullToEmpty(String)";
      printHeader(operation);
      printOriginalAndResultStrings("Dustin", Strings.nullToEmpty("Dustin"), operation);
      printOriginalAndResultStrings(null, Strings.nullToEmpty(null), operation);
      printOriginalAndResultStrings("", Strings.nullToEmpty(""), operation);
   }

The output from running the above code (shown in the next screen snapshot) proves that these methods work as we'd expect: converting null to empty String or converting empty String to null.

padStart and padEnd

Another common practice when dealing with Strings in Java (or any other language) is to pad a String to a certain length with a specified character. Guava supports this easily with its Strings.padStart(String,int,char) and Strings.padEnd(String,int,char) methods, which are demonstrated in the following code listing.

padStart and padEnd
   /**
    * Demonstrate Guava Strings.padStart(String,int,char) method on example
    * Strings.
    */
   public static void demoPadStart()
   {
      final String operation = "Strings.padStart(String,int,char)";
      printHeader(operation);
      printOriginalAndResultStrings("Dustin", Strings.padStart("Dustin", 10, '_'), operation);
      /* Do NOT call Strings.padStart(String,int,char) on a null String:
       *    Exception in thread "main" java.lang.NullPointerException
     *         at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:187)
     *         at com.google.common.base.Strings.padStart(Strings.java:97)
       */
      //printOriginalAndResultStrings(null, Strings.padStart(null, 10, '_'), operation);
      printOriginalAndResultStrings("", Strings.padStart("", 10, '_'), operation);      
   }

   /**
    * Demonstrate Guava Strings.padEnd(String,int,char) method on example
    * Strings.
    */
   public static void demoPadEnd()
   {
      final String operation = "Strings.padEnd(String,int,char)";
      printHeader(operation);
      printOriginalAndResultStrings("Dustin", Strings.padEnd("Dustin", 10, '_'), operation);
      /*
       * Do NOT call Strings.padEnd(String,int,char) on a null String:
       *    Exception in thread "main" java.lang.NullPointerException
     *         at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:187)
     *         at com.google.common.base.Strings.padEnd(Strings.java:129)
       */
      //printOriginalAndResultStrings(null, Strings.padEnd(null, 10, '_'), operation);
      printOriginalAndResultStrings("", Strings.padEnd("", 10, '_'), operation);       
   }

When executed, the above code pads the provided Strings with underscore characters either before or after the provided String depending on which method was called. In both cases, the length of the String was specified as ten. This output is shown in the next screen snapshot.

repeat

A final manipulation technique that Guava's Strings class supports is the ability to easily repeat a given String a specified number of times. This is demonstrated in the next code listing and the corresponding screen snapshot with that code's output. In this example, the provided String is repeated three times.

repeat
   /** Demonstrate Guava Strings.repeat(String,int) method on example Strings. */
   public static void demoRepeat()
   {
      final String operation = "Strings.repeat(String,int)";
      printHeader(operation);
      printOriginalAndResultStrings("Dustin", Strings.repeat("Dustin", 3), operation);
      /*
       * Do NOT call Strings.repeat(String,int) on a null String:
       *    Exception in thread "main" java.lang.NullPointerException
     *         at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:187)
       *         at com.google.common.base.Strings.repeat(Strings.java:153)
       */
      //printOriginalAndResultStrings(null, Strings.repeat(null, 3), operation);
      printOriginalAndResultStrings("", Strings.repeat("", 3), operation);
   }
Wrapping Up

The above examples are simple because Guava's Strings class is simple to use. The complete class containing the demonstration code shown earlier is now listed.

GuavaStrings.java
package dustin.examples;

import com.google.common.base.Strings;
import static java.lang.System.out;

/**
 * Simple demonstration of Guava's Strings class.
 * 
 * @author Dustin
 */
public class GuavaStrings
{
   /**
    * Print to standard output a string message indicating whether the provided
    * String is null or empty or not null or empty. This method uses Guava's
    * Strings.isNullOrEmpty(String) method.
    * 
    * @param string String to be tested for null or empty.
    */
   private static void printStringStatusNullOrEmpty(final String string)
   {
      out.println(  "String '" + string + "' "
                  + (Strings.isNullOrEmpty(string) ? "IS" : "is NOT")
                  + " null or empty.");
   }

   /**
    * Demonstrate Guava Strings.isNullOrEmpty(String) method on some example
    * Strings.
    */
   public static void demoIsNullOrEmpty()
   {
      printHeader("Strings.isNullOrEmpty(String)");
      printStringStatusNullOrEmpty("Dustin");
      printStringStatusNullOrEmpty(null);
      printStringStatusNullOrEmpty("");
   }

   /**
    * Print to standard output a simple message indicating the provided original
    * String and the provided result/output String.
    * 
    * @param originalString Original String.
    * @param resultString Output or result String created by operation.
    * @param operation The operation that acted upon the originalString to create
    *    the resultString.
    */
   private static void printOriginalAndResultStrings(
      final String originalString, final String resultString, final String operation)
   {
      out.println("Passing '" + originalString + "' to " + operation + " produces '" + resultString + "'");
   }

   /** Demonstrate Guava Strings.emptyToNull() method on example Strings. */
   public static void demoEmptyToNull()
   {
      final String operation = "Strings.emptyToNull(String)";
      printHeader(operation);
      printOriginalAndResultStrings("Dustin", Strings.emptyToNull("Dustin"), operation);
      printOriginalAndResultStrings(null, Strings.emptyToNull(null), operation);
      printOriginalAndResultStrings("", Strings.emptyToNull(""), operation);
   }

   /** Demonstrate Guava Strings.nullToEmpty() method on example Strings. */
   public static void demoNullToEmpty()
   {
      final String operation = "Strings.nullToEmpty(String)";
      printHeader(operation);
      printOriginalAndResultStrings("Dustin", Strings.nullToEmpty("Dustin"), operation);
      printOriginalAndResultStrings(null, Strings.nullToEmpty(null), operation);
      printOriginalAndResultStrings("", Strings.nullToEmpty(""), operation);
   }

   /**
    * Demonstrate Guava Strings.padStart(String,int,char) method on example
    * Strings.
    */
   public static void demoPadStart()
   {
      final String operation = "Strings.padStart(String,int,char)";
      printHeader(operation);
      printOriginalAndResultStrings("Dustin", Strings.padStart("Dustin", 10, '_'), operation);
      /* Do NOT call Strings.padStart(String,int,char) on a null String:
       *    Exception in thread "main" java.lang.NullPointerException
     *         at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:187)
     *         at com.google.common.base.Strings.padStart(Strings.java:97)
       */
      //printOriginalAndResultStrings(null, Strings.padStart(null, 10, '_'), operation);
      printOriginalAndResultStrings("", Strings.padStart("", 10, '_'), operation);      
   }

   /**
    * Demonstrate Guava Strings.padEnd(String,int,char) method on example
    * Strings.
    */
   public static void demoPadEnd()
   {
      final String operation = "Strings.padEnd(String,int,char)";
      printHeader(operation);
      printOriginalAndResultStrings("Dustin", Strings.padEnd("Dustin", 10, '_'), operation);
      /*
       * Do NOT call Strings.padEnd(String,int,char) on a null String:
       *    Exception in thread "main" java.lang.NullPointerException
     *         at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:187)
     *         at com.google.common.base.Strings.padEnd(Strings.java:129)
       */
      //printOriginalAndResultStrings(null, Strings.padEnd(null, 10, '_'), operation);
      printOriginalAndResultStrings("", Strings.padEnd("", 10, '_'), operation);       
   }

   /** Demonstrate Guava Strings.repeat(String,int) method on example Strings. */
   public static void demoRepeat()
   {
      final String operation = "Strings.repeat(String,int)";
      printHeader(operation);
      printOriginalAndResultStrings("Dustin", Strings.repeat("Dustin", 3), operation);
      /*
       * Do NOT call Strings.repeat(String,int) on a null String:
       *    Exception in thread "main" java.lang.NullPointerException
     *         at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:187)
       *         at com.google.common.base.Strings.repeat(Strings.java:153)
       */
      //printOriginalAndResultStrings(null, Strings.repeat(null, 3), operation);
      printOriginalAndResultStrings("", Strings.repeat("", 3), operation);
   }

   /**
    * Print a separation header to standard output.
    * 
    * @param headerText Text to be placed in separation header.
    */
   public static void printHeader(final String headerText)
   {
      out.println("\n=========================================================");
      out.println("= " + headerText);
      out.println("=========================================================");
   }

   /**
    * Main function for demonstrating Guava's Strings class.
    * 
    * @param arguments Command-line arguments: none anticipated.
    */
   public static void main(final String[] arguments)
   {
      demoIsNullOrEmpty();
      demoEmptyToNull();
      demoNullToEmpty();
      demoPadStart();
      demoPadEnd();
      demoRepeat();
   }
}

The methods for padding and for repeating Strings do not take kindly to null Strings being passed to them. Indeed, passing a null to these three methods leads to NullPointerExceptions being thrown. Interestingly, these are more examples of Guava using the Preconditions class in its own code.

Conclusion

Many Java libraries and frameworks provide String manipulation functionality is classes with names like StringUtil. Guava's Strings class is one such example and the methods it supplies can make Java manipulation of Strings easier and more concise. Indeed, as I use Guava's Strings's methods, I feel almost like I'm using some of Groovy's GDK String goodness.